diff --git a/docs/KEYBINDINGS_LINUX.md b/docs/KEYBINDINGS_LINUX.md
index 50b44148..b9341958 100644
--- a/docs/KEYBINDINGS_LINUX.md
+++ b/docs/KEYBINDINGS_LINUX.md
@@ -8,7 +8,7 @@ MarkText key bindings for Linux. Please see [general key bindings](KEYBINDINGS.m
| Id | Default | Description |
|:------------------- | --------------------------------------------- | ------------------------------------- |
-| `file.new-file` | Ctrl+N | New file |
+| `file.new-window` | Ctrl+N | New window |
| `file.new-tab` | Ctrl+T | New tab |
| `file.open-file` | Ctrl+O | Open markdown file |
| `file.open-folder` | Ctrl+Shift+O | Open folder |
diff --git a/docs/KEYBINDINGS_OSX.md b/docs/KEYBINDINGS_OSX.md
index dd34fd9c..b26c4e5d 100644
--- a/docs/KEYBINDINGS_OSX.md
+++ b/docs/KEYBINDINGS_OSX.md
@@ -17,7 +17,7 @@ MarkText key bindings for macOS. Please see [general key bindings](KEYBINDINGS.m
| Id | Default | Description |
|:------------------- | ------------------------------------------------ | ------------------------------------- |
-| `file.new-file` | Command+N | New file |
+| `file.new-window` | Command+N | New window |
| `file.new-tab` | Command+T | New tab |
| `file.open-file` | Command+O | Open markdown file |
| `file.open-folder` | Command+Shift+O | Open folder |
diff --git a/docs/KEYBINDINGS_WINDOWS.md b/docs/KEYBINDINGS_WINDOWS.md
index efc87c6b..c639686c 100644
--- a/docs/KEYBINDINGS_WINDOWS.md
+++ b/docs/KEYBINDINGS_WINDOWS.md
@@ -8,7 +8,7 @@ MarkText key bindings for Windows. Please see [general key bindings](KEYBINDINGS
| Id | Default | Description |
|:------------------- | --------------------------------------------- | ------------------------------------- |
-| `file.new-file` | Ctrl+N | New file |
+| `file.new-window` | Ctrl+N | New window |
| `file.new-tab` | Ctrl+T | New tab |
| `file.open-file` | Ctrl+O | Open markdown file |
| `file.open-folder` | Ctrl+Shift+O | Open folder |
diff --git a/src/common/commands/constants.js b/src/common/commands/constants.js
new file mode 100644
index 00000000..aa869497
--- /dev/null
+++ b/src/common/commands/constants.js
@@ -0,0 +1,110 @@
+const COMMANDS = Object.freeze({
+ EDIT_COPY: 'edit.copy',
+ EDIT_COPY_AS_HTML: 'edit.copy-as-html',
+ EDIT_COPY_AS_MARKDOWN: 'edit.copy-as-markdown',
+ EDIT_CREATE_PARAGRAPH: 'edit.create-paragraph',
+ EDIT_CUT: 'edit.cut',
+ EDIT_DELETE_PARAGRAPH: 'edit.delete-paragraph',
+ EDIT_DUPLICATE: 'edit.duplicate',
+ EDIT_FIND: 'edit.find',
+ EDIT_FIND_IN_FOLDER: 'edit.find-in-folder',
+ EDIT_FIND_NEXT: 'edit.find-next',
+ EDIT_FIND_PREVIOUS: 'edit.find-previous',
+ EDIT_PASTE: 'edit.paste',
+ EDIT_PASTE_AS_PLAINTEXT: 'edit.paste-as-plaintext',
+ EDIT_REDO: 'edit.redo',
+ EDIT_REPLACE: 'edit.replace',
+ EDIT_SCREENSHOT: 'edit.screenshot',
+ EDIT_SELECT_ALL: 'edit.select-all',
+ EDIT_UNDO: 'edit.undo',
+
+ FILE_CHECK_UPDATE: 'file.check-update',
+ FILE_CLOSE_TAB: 'file.close-tab',
+ FILE_CLOSE_WINDOW: 'file.close-window',
+ FILE_EXPORT_FILE: 'file.export-file',
+ FILE_IMPORT_FILE: 'file.import-file',
+ FILE_MOVE_FILE: 'file.move-file',
+ FILE_NEW_FILE: 'file.new-window',
+ FILE_NEW_TAB: 'file.new-tab',
+ FILE_OPEN_FILE: 'file.open-file',
+ FILE_OPEN_FOLDER: 'file.open-folder',
+ FILE_PREFERENCES: 'file.preferences',
+ FILE_PRINT: 'file.print',
+ FILE_QUICK_OPEN: 'file.quick-open',
+ FILE_QUIT: 'file.quit',
+ FILE_RENAME_FILE: 'file.rename-file',
+ FILE_SAVE: 'file.save',
+ FILE_SAVE_AS: 'file.save-as',
+ // FILE_TOGGLE_AUTO_SAVE: 'file.toggle-auto-save',
+
+ FORMAT_CLEAR_FORMAT: 'format.clear-format',
+ FORMAT_EMPHASIS: 'format.emphasis',
+ FORMAT_HIGHLIGHT: 'format.highlight',
+ FORMAT_HYPERLINK: 'format.hyperlink',
+ FORMAT_IMAGE: 'format.image',
+ FORMAT_INLINE_CODE: 'format.inline-code',
+ FORMAT_INLINE_MATH: 'format.inline-math',
+ FORMAT_STRIKE: 'format.strike',
+ FORMAT_STRONG: 'format.strong',
+ FORMAT_SUBSCRIPT: 'format.subscript',
+ FORMAT_SUPERSCRIPT: 'format.superscript',
+ FORMAT_UNDERLINE: 'format.underline',
+
+ MT_HIDE: 'mt.hide',
+ MT_HIDE_OTHERS: 'mt.hide-others',
+
+ PARAGRAPH_BULLET_LIST: 'paragraph.bullet-list',
+ PARAGRAPH_CODE_FENCE: 'paragraph.code-fence',
+ PARAGRAPH_DEGRADE_HEADING: 'paragraph.degrade-heading',
+ PARAGRAPH_FRONT_MATTER: 'paragraph.front-matter',
+ PARAGRAPH_HEADING_1: 'paragraph.heading-1',
+ PARAGRAPH_HEADING_2: 'paragraph.heading-2',
+ PARAGRAPH_HEADING_3: 'paragraph.heading-3',
+ PARAGRAPH_HEADING_4: 'paragraph.heading-4',
+ PARAGRAPH_HEADING_5: 'paragraph.heading-5',
+ PARAGRAPH_HEADING_6: 'paragraph.heading-6',
+ PARAGRAPH_HORIZONTAL_LINE: 'paragraph.horizontal-line',
+ PARAGRAPH_HTML_BLOCK: 'paragraph.html-block',
+ PARAGRAPH_LOOSE_LIST_ITEM: 'paragraph.loose-list-item',
+ PARAGRAPH_MATH_FORMULA: 'paragraph.math-formula',
+ PARAGRAPH_ORDERED_LIST: 'paragraph.order-list',
+ PARAGRAPH_PARAGRAPH: 'paragraph.paragraph',
+ PARAGRAPH_QUOTE_BLOCK: 'paragraph.quote-block',
+ PARAGRAPH_TABLE: 'paragraph.table',
+ PARAGRAPH_TASK_LIST: 'paragraph.task-list',
+ PARAGRAPH_INCREASE_HEADING: 'paragraph.upgrade-heading',
+
+ TABS_CYCLE_BACKWARD: 'tabs.cycle-backward',
+ TABS_CYCLE_FORWARD: 'tabs.cycle-forward',
+ TABS_SWITCH_TO_EIGHTH: 'tabs.switch-to-eighth',
+ TABS_SWITCH_TO_FIFTH: 'tabs.switch-to-fifth',
+ TABS_SWITCH_TO_FIRST: 'tabs.switch-to-first',
+ TABS_SWITCH_TO_FOURTH: 'tabs.switch-to-fourth',
+ TABS_SWITCH_TO_LEFT: 'tabs.switch-to-left',
+ TABS_SWITCH_TO_NINTH: 'tabs.switch-to-ninth',
+ TABS_SWITCH_TO_RIGHT: 'tabs.switch-to-right',
+ TABS_SWITCH_TO_SECOND: 'tabs.switch-to-second',
+ TABS_SWITCH_TO_SEVENTH: 'tabs.switch-to-seventh',
+ TABS_SWITCH_TO_SIXTH: 'tabs.switch-to-sixth',
+ TABS_SWITCH_TO_TENTH: 'tabs.switch-to-tenth',
+ TABS_SWITCH_TO_THIRD: 'tabs.switch-to-third',
+
+ VIEW_COMMAND_PALETTE: 'view.command-palette',
+ VIEW_DEV_RELOAD: 'view.dev-reload',
+ VIEW_FOCUS_MODE: 'view.focus-mode',
+ VIEW_FORCE_RELOAD_IMAGES: 'view.reload-images',
+ VIEW_SOURCE_CODE_MODE: 'view.source-code-mode',
+ VIEW_TOGGLE_DEV_TOOLS: 'view.toggle-dev-tools',
+ VIEW_TOGGLE_SIDEBAR: 'view.toggle-sidebar',
+ VIEW_TOGGLE_TABBAR: 'view.toggle-tabbar',
+ VIEW_TOGGLE_TOC: 'view.toggle-toc',
+ VIEW_TYPEWRITER_MODE: 'view.typewriter-mode',
+
+ WINDOW_MINIMIZE: 'window.minimize',
+ WINDOW_TOGGLE_ALWAYS_ON_TOP: 'window.toggle-always-on-top',
+ WINDOW_TOGGLE_FULL_SCREEN: 'window.toggle-full-screen',
+ WINDOW_ZOOM_IN: 'window.zoom-in',
+ WINDOW_ZOOM_OUT: 'window.zoom-out'
+})
+
+export default COMMANDS
diff --git a/src/main/app/accessor.js b/src/main/app/accessor.js
index 6a701735..f071a322 100644
--- a/src/main/app/accessor.js
+++ b/src/main/app/accessor.js
@@ -3,6 +3,8 @@ import Preference from '../preferences'
import DataCenter from '../dataCenter'
import Keybindings from '../keyboard/shortcutHandler'
import AppMenu from '../menu'
+import { loadMenuCommands } from '../menu/actions'
+import { CommandManager, loadDefaultCommands } from '../commands'
class Accessor {
/**
@@ -13,12 +15,27 @@ class Accessor {
this.env = appEnvironment
this.paths = appEnvironment.paths // export paths to make it better accessible
+
this.preferences = new Preference(this.paths)
this.dataCenter = new DataCenter(this.paths)
- this.keybindings = new Keybindings(userDataPath)
+
+ this.commandManager = CommandManager
+ this._loadCommands()
+
+ this.keybindings = new Keybindings(this.commandManager, appEnvironment)
this.menu = new AppMenu(this.preferences, this.keybindings, userDataPath)
this.windowManager = new WindowManager(this.menu, this.preferences)
}
+
+ _loadCommands () {
+ const { commandManager } = this
+ loadDefaultCommands(commandManager)
+ loadMenuCommands(commandManager)
+
+ if (this.env.isDevMode) {
+ commandManager.__verifyDefaultCommands()
+ }
+ }
}
export default Accessor
diff --git a/src/main/app/env.js b/src/main/app/env.js
index e47e7b2a..50c77058 100644
--- a/src/main/app/env.js
+++ b/src/main/app/env.js
@@ -14,6 +14,7 @@ export class AppEnvironment {
this._id = envId++
this._appPaths = new AppPaths(options.userDataPath)
this._debug = !!options.debug
+ this._isDevMode = !!options.isDevMode
this._verbose = !!options.verbose
this._safeMode = !!options.safeMode
}
@@ -41,6 +42,13 @@ export class AppEnvironment {
return this._debug
}
+ /**
+ * @returns {boolean}
+ */
+ get isDevMode () {
+ return this._isDevMode
+ }
+
/**
* @returns {boolean}
*/
@@ -65,6 +73,7 @@ export class AppEnvironment {
const setupEnvironment = args => {
patchEnvPath()
+ const isDevMode = process.env.NODE_ENV !== 'production'
const debug = args['--debug'] || !!process.env.MARKTEXT_DEBUG || process.env.NODE_ENV !== 'production'
const verbose = args['--verbose'] || 0
const safeMode = args['--safe']
@@ -72,6 +81,7 @@ const setupEnvironment = args => {
const appEnvironment = new AppEnvironment({
debug,
+ isDevMode,
verbose,
safeMode,
userDataPath
diff --git a/src/main/commands/file.js b/src/main/commands/file.js
new file mode 100644
index 00000000..ffe81087
--- /dev/null
+++ b/src/main/commands/file.js
@@ -0,0 +1,11 @@
+import { COMMANDS } from './index'
+
+const openQuickOpenDialog = win => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::execute-command-by-id', 'file.quick-open')
+ }
+}
+
+export const loadFileCommands = commandManager => {
+ commandManager.add(COMMANDS.FILE_QUICK_OPEN, openQuickOpenDialog)
+}
diff --git a/src/main/commands/index.js b/src/main/commands/index.js
new file mode 100644
index 00000000..3ae07978
--- /dev/null
+++ b/src/main/commands/index.js
@@ -0,0 +1,53 @@
+import COMMAND_CONSTANTS from 'common/commands/constants'
+import { loadFileCommands } from './file'
+import { loadTabCommands } from './tab'
+
+export const COMMANDS = COMMAND_CONSTANTS
+
+export const loadDefaultCommands = commandManager => {
+ loadFileCommands(commandManager)
+ loadTabCommands(commandManager)
+}
+
+class CommandManager {
+ constructor () {
+ this._commands = new Map()
+ }
+
+ add (id, callback) {
+ const { _commands } = this
+ if (_commands.has(id)) {
+ throw new Error(`Command with id="${id}" already exists.`)
+ }
+ _commands.set(id, callback)
+ }
+
+ remove (id) {
+ return this._commands.delete(id)
+ }
+
+ has (id) {
+ return this._commands.has(id)
+ }
+
+ execute (id, ...args) {
+ const command = this._commands.get(id)
+ if (!command) {
+ throw new Error(`No command found with id="${id}".`)
+ }
+ return command(...args)
+ }
+
+ __verifyDefaultCommands () {
+ const { _commands } = this
+ Object.keys(COMMANDS).forEach(propertyName => {
+ const id = COMMANDS[propertyName]
+ if (!_commands.has(id)) {
+ console.error(`[DEBUG] Default command with id="${id}" isn't available!`)
+ }
+ })
+ }
+}
+
+const commandManagerInstance = new CommandManager()
+export { commandManagerInstance as CommandManager }
diff --git a/src/main/commands/tab.js b/src/main/commands/tab.js
new file mode 100644
index 00000000..28b3ea59
--- /dev/null
+++ b/src/main/commands/tab.js
@@ -0,0 +1,36 @@
+import { COMMANDS } from './index'
+
+const switchToLeftTab = win => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::tabs-cycle-left')
+ }
+}
+
+const switchToRightTab = win => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::tabs-cycle-right')
+ }
+}
+
+const switchTabByIndex = (win, index) => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::switch-tab-by-index', index)
+ }
+}
+
+export const loadTabCommands = commandManager => {
+ commandManager.add(COMMANDS.TABS_CYCLE_BACKWARD, switchToLeftTab)
+ commandManager.add(COMMANDS.TABS_CYCLE_FORWARD, switchToRightTab)
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_LEFT, switchToLeftTab)
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_RIGHT, switchToRightTab)
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_FIRST, win => switchTabByIndex(win, 0))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_SECOND, win => switchTabByIndex(win, 1))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_THIRD, win => switchTabByIndex(win, 2))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_FOURTH, win => switchTabByIndex(win, 3))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_FIFTH, win => switchTabByIndex(win, 4))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_SIXTH, win => switchTabByIndex(win, 5))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_SEVENTH, win => switchTabByIndex(win, 6))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_EIGHTH, win => switchTabByIndex(win, 7))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_NINTH, win => switchTabByIndex(win, 8))
+ commandManager.add(COMMANDS.TABS_SWITCH_TO_TENTH, win => switchTabByIndex(win, 9))
+}
diff --git a/src/main/keyboard/keybindingsDarwin.js b/src/main/keyboard/keybindingsDarwin.js
index 335ac85b..03963aa8 100644
--- a/src/main/keyboard/keybindingsDarwin.js
+++ b/src/main/keyboard/keybindingsDarwin.js
@@ -10,7 +10,7 @@ export default new Map([
['file.preferences', 'Command+,'], // located under MarkText menu in macOS only
// File menu
- ['file.new-file', 'Command+N'],
+ ['file.new-window', 'Command+N'],
['file.new-tab', 'Command+T'],
['file.open-file', 'Command+O'],
['file.open-folder', 'Command+Shift+O'],
diff --git a/src/main/keyboard/keybindingsLinux.js b/src/main/keyboard/keybindingsLinux.js
index 8e4303ae..6329b2f8 100644
--- a/src/main/keyboard/keybindingsLinux.js
+++ b/src/main/keyboard/keybindingsLinux.js
@@ -13,7 +13,7 @@ export default new Map([
['mt.hide-others', ''],
// File menu
- ['file.new-file', 'Ctrl+N'],
+ ['file.new-window', 'Ctrl+N'],
['file.new-tab', 'Ctrl+T'],
['file.open-file', 'Ctrl+O'],
['file.open-folder', 'Ctrl+Shift+O'],
diff --git a/src/main/keyboard/keybindingsWindows.js b/src/main/keyboard/keybindingsWindows.js
index c4c6200c..eaada384 100644
--- a/src/main/keyboard/keybindingsWindows.js
+++ b/src/main/keyboard/keybindingsWindows.js
@@ -10,7 +10,7 @@ export default new Map([
['mt.hide-others', ''],
// File menu
- ['file.new-file', 'Ctrl+N'],
+ ['file.new-window', 'Ctrl+N'],
['file.new-tab', 'Ctrl+T'],
['file.open-file', 'Ctrl+O'],
['file.open-folder', 'Ctrl+Shift+O'],
diff --git a/src/main/keyboard/shortcutHandler.js b/src/main/keyboard/shortcutHandler.js
index c76d59f5..fbd4dd41 100644
--- a/src/main/keyboard/shortcutHandler.js
+++ b/src/main/keyboard/shortcutHandler.js
@@ -1,4 +1,4 @@
-import { shell, Menu } from 'electron'
+import { shell } from 'electron'
import fs from 'fs'
import fsPromises from 'fs/promises'
import path from 'path'
@@ -14,15 +14,26 @@ import keybindingsWindows from './keybindingsWindows'
class Keybindings {
/**
- * @param {string} userDataPath The user data path.
+ * @param {CommandManager} commandManager The command manager instance.
+ * @param {AppEnvironment} appEnvironment The application environment instance.
*/
- constructor (userDataPath) {
+ constructor (commandManager, appEnvironment) {
+ const { userDataPath } = appEnvironment.paths
this.configPath = path.join(userDataPath, 'keybindings.json')
+ this.commandManager = commandManager
this.userKeybindings = new Map()
this.keys = this.getDefaultKeybindings()
this._prepareKeyMapper()
+ if (appEnvironment.isDevMode) {
+ for (const [id, accelerator] of this.keys) {
+ if (!commandManager.has(id)) {
+ console.error(`[DEBUG] Command with id="${id}" isn't available for accelerator="${accelerator}".`)
+ }
+ }
+ }
+
// Load user-defined keybindings
this._loadLocalKeybindings()
}
@@ -35,21 +46,32 @@ class Keybindings {
return name
}
- registerKeyHandlers (win, acceleratorMap) {
- for (const item of acceleratorMap) {
- let { accelerator } = item
- if (accelerator == null || accelerator === '') {
- continue
- }
+ registerAccelerator (win, accelerator, callback) {
+ if (!win || !accelerator || !callback) {
+ throw new Error(`addKeyHandler: invalid arguments (accelerator="${accelerator}").`)
+ }
- // Regisiter shortcuts on the BrowserWindow instead of using Chromium's native menu.
- // This makes it possible to receive key down events before Chromium/Electron and we
- // can handle reserved Chromium shortcuts. Afterwards prevent the default action of
- // the event so the native menu is not triggered.
- electronLocalshortcut.register(win, accelerator, () => {
- callMenuCallback(item, win)
- return true // prevent default action
- })
+ // Register shortcuts on the BrowserWindow instead of using Chromium's native menu.
+ // This makes it possible to receive key down events before Chromium/Electron and we
+ // can handle reserved Chromium shortcuts. Afterwards prevent the default action of
+ // the event so the native menu is not triggered.
+ electronLocalshortcut.register(win, accelerator, () => {
+ callback(win)
+ return true // prevent default action
+ })
+ }
+
+ unregisterAccelerator (win, accelerator) {
+ electronLocalshortcut.unregister(win, accelerator)
+ }
+
+ registerEditorKeyHandlers (win) {
+ for (const [id, accelerator] of this.keys) {
+ if (accelerator && accelerator.length > 1) {
+ this.registerAccelerator(win, accelerator, () => {
+ this.commandManager.execute(id, win)
+ })
+ }
}
}
@@ -213,43 +235,4 @@ class Keybindings {
}
}
-export const parseMenu = menuTemplate => {
- const { submenu, accelerator, click, id, visible } = menuTemplate
- const items = []
- if (Array.isArray(menuTemplate)) {
- for (const item of menuTemplate) {
- const subitems = parseMenu(item)
- if (subitems) items.push(...subitems)
- }
- } else if (submenu) {
- const subitems = parseMenu(submenu)
- if (subitems) items.push(...subitems)
- } else if ((visible === undefined || visible) && accelerator && click) {
- items.push({
- accelerator,
- click,
- id // may be null
- })
- }
- return items.length === 0 ? null : items
-}
-
-const callMenuCallback = (menuInfo, win) => {
- const { click, id } = menuInfo
- if (click) {
- let menuItem = null
- if (id) {
- const menus = Menu.getApplicationMenu()
- menuItem = menus.getMenuItemById(id)
- }
-
- // Allow all shortcuts/menus without id and only enabled menus with id (GH#980).
- if (!menuItem || menuItem.enabled !== false) {
- click(menuItem, win)
- }
- } else {
- console.error('ERROR: callback function is not defined.')
- }
-}
-
export default Keybindings
diff --git a/src/main/menu/actions/edit.js b/src/main/menu/actions/edit.js
index 19ecde9e..37460457 100644
--- a/src/main/menu/actions/edit.js
+++ b/src/main/menu/actions/edit.js
@@ -1,8 +1,10 @@
import path from 'path'
import { ipcMain, BrowserWindow } from 'electron'
import log from 'electron-log'
+import { COMMANDS } from '../../commands'
import { searchFilesAndDir } from '../../utils/imagePathAutoComplement'
+// TODO(Refactor): Move to filesystem and provide generic API to search files in directories.
ipcMain.on('mt::ask-for-image-auto-path', (e, { pathname, src, id }) => {
const win = BrowserWindow.fromWebContents(e.sender)
if (!src || typeof src !== 'string') {
@@ -26,6 +28,64 @@ ipcMain.on('mt::ask-for-image-auto-path', (e, { pathname, src, id }) => {
})
})
+// --- Menu actions -------------------------------------------------------------
+
+export const editorUndo = win => {
+ edit(win, 'undo')
+}
+
+export const editorRedo = win => {
+ edit(win, 'redo')
+}
+
+export const editorCopyAsMarkdown = win => {
+ edit(win, 'copyAsMarkdown')
+}
+
+export const editorCopyAsHtml = win => {
+ edit(win, 'copyAsHtml')
+}
+
+export const editorPasteAsPlainText = win => {
+ edit(win, 'pasteAsPlainText')
+}
+
+export const editorSelectAll = win => {
+ edit(win, 'selectAll')
+}
+
+export const editorDuplicate = win => {
+ edit(win, 'duplicate')
+}
+
+export const editorCreateParagraph = win => {
+ edit(win, 'createParagraph')
+}
+
+export const editorDeleteParagraph = win => {
+ edit(win, 'deleteParagraph')
+}
+
+export const editorFind = win => {
+ edit(win, 'find')
+}
+
+export const editorFindNext = win => {
+ edit(win, 'findNext')
+}
+
+export const editorFindPrevious = win => {
+ edit(win, 'findPrev')
+}
+
+export const editorReplace = win => {
+ edit(win, 'undo')
+}
+
+export const findInFolder = win => {
+ edit(win, 'findInFolder')
+}
+
export const edit = (win, type) => {
if (win && win.webContents) {
win.webContents.send('mt::editor-edit-action', type)
@@ -60,6 +120,29 @@ export const lineEnding = (win, lineEnding) => {
}
}
+// --- Commands -------------------------------------------------------------
+
+export const loadEditCommands = commandManager => {
+ commandManager.add(COMMANDS.EDIT_COPY, nativeCopy)
+ commandManager.add(COMMANDS.EDIT_COPY_AS_HTML, editorCopyAsHtml)
+ commandManager.add(COMMANDS.EDIT_COPY_AS_MARKDOWN, editorCopyAsMarkdown)
+ commandManager.add(COMMANDS.EDIT_CREATE_PARAGRAPH, editorCreateParagraph)
+ commandManager.add(COMMANDS.EDIT_CUT, nativeCut)
+ commandManager.add(COMMANDS.EDIT_DELETE_PARAGRAPH, editorDeleteParagraph)
+ commandManager.add(COMMANDS.EDIT_DUPLICATE, editorDuplicate)
+ commandManager.add(COMMANDS.EDIT_FIND, editorFind)
+ commandManager.add(COMMANDS.EDIT_FIND_IN_FOLDER, findInFolder)
+ commandManager.add(COMMANDS.EDIT_FIND_NEXT, editorFindNext)
+ commandManager.add(COMMANDS.EDIT_FIND_PREVIOUS, editorFindPrevious)
+ commandManager.add(COMMANDS.EDIT_PASTE, nativePaste)
+ commandManager.add(COMMANDS.EDIT_PASTE_AS_PLAINTEXT, editorPasteAsPlainText)
+ commandManager.add(COMMANDS.EDIT_REDO, editorRedo)
+ commandManager.add(COMMANDS.EDIT_REPLACE, editorReplace)
+ commandManager.add(COMMANDS.EDIT_SCREENSHOT, screenshot)
+ commandManager.add(COMMANDS.EDIT_SELECT_ALL, editorSelectAll)
+ commandManager.add(COMMANDS.EDIT_UNDO, editorUndo)
+}
+
// --- IPC events -------------------------------------------------------------
// NOTE: Don't use static `getMenuItemById` here, instead request the menu by
diff --git a/src/main/menu/actions/file.js b/src/main/menu/actions/file.js
index 4197d811..b743735a 100644
--- a/src/main/menu/actions/file.js
+++ b/src/main/menu/actions/file.js
@@ -1,9 +1,12 @@
import fs from 'fs-extra'
import path from 'path'
-import { BrowserWindow, dialog, ipcMain, shell } from 'electron'
+import { BrowserWindow, app, dialog, ipcMain, shell } from 'electron'
import log from 'electron-log'
import { isDirectory, isFile, exists } from 'common/filesystem'
import { MARKDOWN_EXTENSIONS, isMarkdownFile } from 'common/filesystem/paths'
+import { checkUpdates, userSetting } from './marktext'
+import { showTabBar } from './view'
+import { COMMANDS } from '../../commands'
import { EXTENSION_HASN, PANDOC_EXTENSIONS, URL_REG } from '../../config'
import { normalizeAndResolvePath, writeFile } from '../../filesystem'
import { writeMarkdownFile } from '../../filesystem/markdown'
@@ -501,7 +504,7 @@ export const importFile = async win => {
}
}
-export const print = win => {
+export const printDocument = win => {
if (win) {
win.webContents.send('mt::show-export-dialog', 'print')
}
@@ -545,6 +548,7 @@ export const openFileOrFolder = (win, pathname) => {
export const newBlankTab = win => {
if (win && win.webContents) {
win.webContents.send('mt::new-untitled-tab')
+ showTabBar(win)
}
}
@@ -596,3 +600,24 @@ export const rename = win => {
export const clearRecentlyUsed = () => {
ipcMain.emit('menu-clear-recently-used')
}
+
+// --- Commands -------------------------------------------------------------
+
+export const loadFileCommands = commandManager => {
+ commandManager.add(COMMANDS.FILE_CHECK_UPDATE, checkUpdates)
+ commandManager.add(COMMANDS.FILE_CLOSE_TAB, closeTab)
+ commandManager.add(COMMANDS.FILE_CLOSE_WINDOW, closeWindow)
+ commandManager.add(COMMANDS.FILE_EXPORT_FILE, exportFile)
+ commandManager.add(COMMANDS.FILE_IMPORT_FILE, importFile)
+ commandManager.add(COMMANDS.FILE_MOVE_FILE, moveTo)
+ commandManager.add(COMMANDS.FILE_NEW_FILE, newEditorWindow)
+ commandManager.add(COMMANDS.FILE_NEW_TAB, newBlankTab)
+ commandManager.add(COMMANDS.FILE_OPEN_FILE, openFile)
+ commandManager.add(COMMANDS.FILE_OPEN_FOLDER, openFolder)
+ commandManager.add(COMMANDS.FILE_PREFERENCES, userSetting)
+ commandManager.add(COMMANDS.FILE_PRINT, printDocument)
+ commandManager.add(COMMANDS.FILE_QUIT, app.quit)
+ commandManager.add(COMMANDS.FILE_RENAME_FILE, rename)
+ commandManager.add(COMMANDS.FILE_SAVE, save)
+ commandManager.add(COMMANDS.FILE_SAVE_AS, saveAs)
+}
diff --git a/src/main/menu/actions/format.js b/src/main/menu/actions/format.js
index 34124b16..13eca57d 100644
--- a/src/main/menu/actions/format.js
+++ b/src/main/menu/actions/format.js
@@ -1,4 +1,6 @@
-const MENU_ID_FORMAT_MAP = {
+import { COMMANDS } from '../../commands'
+
+const MENU_ID_FORMAT_MAP = Object.freeze({
strongMenuItem: 'strong',
emphasisMenuItem: 'em',
inlineCodeMenuItem: 'inline_code',
@@ -6,14 +8,79 @@ const MENU_ID_FORMAT_MAP = {
hyperlinkMenuItem: 'link',
imageMenuItem: 'image',
inlineMathMenuItem: 'inline_math'
-}
+})
-export const format = (win, type) => {
+const format = (win, type) => {
if (win && win.webContents) {
win.webContents.send('mt::editor-format-action', { type })
}
}
+export const clearFormat = win => {
+ format(win, 'clear')
+}
+
+export const emphasis = win => {
+ format(win, 'em')
+}
+
+export const highlight = win => {
+ format(win, 'mark')
+}
+
+export const hyperlink = win => {
+ format(win, 'link')
+}
+
+export const image = win => {
+ format(win, 'image')
+}
+
+export const inlineCode = win => {
+ format(win, 'inline_code')
+}
+
+export const inlineMath = win => {
+ format(win, 'inline_math')
+}
+
+export const strikethrough = win => {
+ format(win, 'del')
+}
+
+export const strong = win => {
+ format(win, 'strong')
+}
+
+export const subscript = win => {
+ format(win, 'sub')
+}
+
+export const superscript = win => {
+ format(win, 'sup')
+}
+
+export const underline = win => {
+ format(win, 'u')
+}
+
+// --- Commands -------------------------------------------------------------
+
+export const loadFormatCommands = commandManager => {
+ commandManager.add(COMMANDS.FORMAT_CLEAR_FORMAT, clearFormat)
+ commandManager.add(COMMANDS.FORMAT_EMPHASIS, emphasis)
+ commandManager.add(COMMANDS.FORMAT_HIGHLIGHT, highlight)
+ commandManager.add(COMMANDS.FORMAT_HYPERLINK, hyperlink)
+ commandManager.add(COMMANDS.FORMAT_IMAGE, image)
+ commandManager.add(COMMANDS.FORMAT_INLINE_CODE, inlineCode)
+ commandManager.add(COMMANDS.FORMAT_INLINE_MATH, inlineMath)
+ commandManager.add(COMMANDS.FORMAT_STRIKE, strikethrough)
+ commandManager.add(COMMANDS.FORMAT_STRONG, strong)
+ commandManager.add(COMMANDS.FORMAT_SUBSCRIPT, subscript)
+ commandManager.add(COMMANDS.FORMAT_SUPERSCRIPT, superscript)
+ commandManager.add(COMMANDS.FORMAT_UNDERLINE, underline)
+}
+
// --- IPC events -------------------------------------------------------------
// NOTE: Don't use static `getMenuItemById` here, instead request the menu by
diff --git a/src/main/menu/actions/index.js b/src/main/menu/actions/index.js
new file mode 100644
index 00000000..187b5c16
--- /dev/null
+++ b/src/main/menu/actions/index.js
@@ -0,0 +1,17 @@
+import { loadEditCommands } from './edit'
+import { loadFileCommands } from './file'
+import { loadFormatCommands } from './format'
+import { loadMarktextCommands } from './marktext'
+import { loadParagraphCommands } from './paragraph'
+import { loadViewCommands } from './view'
+import { loadWindowCommands } from './window'
+
+export const loadMenuCommands = commandManager => {
+ loadEditCommands(commandManager)
+ loadFileCommands(commandManager)
+ loadFormatCommands(commandManager)
+ loadMarktextCommands(commandManager)
+ loadParagraphCommands(commandManager)
+ loadViewCommands(commandManager)
+ loadWindowCommands(commandManager)
+}
diff --git a/src/main/menu/actions/marktext.js b/src/main/menu/actions/marktext.js
index 6fdfb143..8ec9eeea 100644
--- a/src/main/menu/actions/marktext.js
+++ b/src/main/menu/actions/marktext.js
@@ -1,5 +1,7 @@
import { autoUpdater } from 'electron-updater'
-import { ipcMain, BrowserWindow } from 'electron'
+import { ipcMain, BrowserWindow, Menu } from 'electron'
+import { COMMANDS } from '../../commands'
+import { isOsx } from '../../config'
let runningUpdate = false
let win = null
@@ -36,18 +38,6 @@ autoUpdater.on('update-downloaded', () => {
setImmediate(() => autoUpdater.quitAndInstall())
})
-export const userSetting = () => {
- ipcMain.emit('app-create-settings-window')
-}
-
-export const checkUpdates = browserWindow => {
- if (!runningUpdate) {
- runningUpdate = true
- win = browserWindow
- autoUpdater.checkForUpdates()
- }
-}
-
ipcMain.on('mt::NEED_UPDATE', (e, { needUpdate }) => {
if (needUpdate) {
autoUpdater.downloadUpdate()
@@ -60,3 +50,42 @@ ipcMain.on('mt::check-for-update', e => {
const win = BrowserWindow.fromWebContents(e.sender)
checkUpdates(win)
})
+
+// --------------------------------------------------------
+
+export const userSetting = () => {
+ ipcMain.emit('app-create-settings-window')
+}
+
+export const checkUpdates = browserWindow => {
+ if (!runningUpdate) {
+ runningUpdate = true
+ win = browserWindow
+ autoUpdater.checkForUpdates()
+ }
+}
+
+export const osxHide = () => {
+ if (isOsx) {
+ Menu.sendActionToFirstResponder('hide:')
+ }
+}
+
+export const osxHideAll = () => {
+ if (isOsx) {
+ Menu.sendActionToFirstResponder('hideOtherApplications:')
+ }
+}
+
+export const osxShowAll = () => {
+ if (isOsx) {
+ Menu.sendActionToFirstResponder('unhideAllApplications:')
+ }
+}
+
+// --- Commands -------------------------------------------------------------
+
+export const loadMarktextCommands = commandManager => {
+ commandManager.add(COMMANDS.MT_HIDE, osxHide)
+ commandManager.add(COMMANDS.MT_HIDE_OTHERS, osxHideAll)
+}
diff --git a/src/main/menu/actions/paragraph.js b/src/main/menu/actions/paragraph.js
index fa4890b9..7bba4dab 100644
--- a/src/main/menu/actions/paragraph.js
+++ b/src/main/menu/actions/paragraph.js
@@ -1,3 +1,5 @@
+import { COMMANDS } from '../../commands'
+
const DISABLE_LABELS = [
// paragraph menu items
'heading1MenuItem', 'heading2MenuItem', 'heading3MenuItem', 'heading4MenuItem',
@@ -8,7 +10,7 @@ const DISABLE_LABELS = [
'hyperlinkMenuItem', 'imageMenuItem'
]
-const MENU_ID_MAP = {
+const MENU_ID_MAP = Object.freeze({
heading1MenuItem: 'h1',
heading2MenuItem: 'h2',
heading3MenuItem: 'h3',
@@ -26,14 +28,119 @@ const MENU_ID_MAP = {
paragraphMenuItem: 'p',
horizontalLineMenuItem: 'hr',
frontMatterMenuItem: 'frontmatter' // 'pre'
-}
+})
-export const paragraph = (win, type) => {
+const transformEditorElement = (win, type) => {
if (win && win.webContents) {
win.webContents.send('mt::editor-paragraph-action', { type })
}
}
+export const bulletList = win => {
+ transformEditorElement(win, 'ul-bullet')
+}
+
+export const codeFence = win => {
+ transformEditorElement(win, 'pre')
+}
+
+export const degradeHeading = win => {
+ transformEditorElement(win, 'degrade heading')
+}
+
+export const frontMatter = win => {
+ transformEditorElement(win, 'front-matter')
+}
+
+export const heading1 = win => {
+ transformEditorElement(win, 'heading 1')
+}
+
+export const heading2 = win => {
+ transformEditorElement(win, 'heading 2')
+}
+
+export const heading3 = win => {
+ transformEditorElement(win, 'heading 3')
+}
+
+export const heading4 = win => {
+ transformEditorElement(win, 'heading 4')
+}
+
+export const heading5 = win => {
+ transformEditorElement(win, 'heading 5')
+}
+
+export const heading6 = win => {
+ transformEditorElement(win, 'heading 6')
+}
+
+export const horizontalLine = win => {
+ transformEditorElement(win, 'hr')
+}
+
+export const htmlBlock = win => {
+ transformEditorElement(win, 'html')
+}
+
+export const looseListItem = win => {
+ transformEditorElement(win, 'loose-list-item')
+}
+
+export const mathFormula = win => {
+ transformEditorElement(win, 'mathblock')
+}
+
+export const orderedList = win => {
+ transformEditorElement(win, 'ol-order')
+}
+
+export const paragraph = win => {
+ transformEditorElement(win, 'paragraph')
+}
+
+export const quoteBlock = win => {
+ transformEditorElement(win, 'blockquote')
+}
+
+export const table = win => {
+ transformEditorElement(win, 'table')
+}
+
+export const taskList = win => {
+ transformEditorElement(win, 'ul-task')
+}
+
+export const increaseHeading = win => {
+ transformEditorElement(win, 'upgrade heading')
+}
+
+// --- Commands -------------------------------------------------------------
+
+export const loadParagraphCommands = commandManager => {
+ commandManager.add(COMMANDS.PARAGRAPH_BULLET_LIST, bulletList)
+ commandManager.add(COMMANDS.PARAGRAPH_CODE_FENCE, codeFence)
+ commandManager.add(COMMANDS.PARAGRAPH_DEGRADE_HEADING, degradeHeading)
+ commandManager.add(COMMANDS.PARAGRAPH_FRONT_MATTER, frontMatter)
+ commandManager.add(COMMANDS.PARAGRAPH_HEADING_1, heading1)
+ commandManager.add(COMMANDS.PARAGRAPH_HEADING_2, heading2)
+ commandManager.add(COMMANDS.PARAGRAPH_HEADING_3, heading3)
+ commandManager.add(COMMANDS.PARAGRAPH_HEADING_4, heading4)
+ commandManager.add(COMMANDS.PARAGRAPH_HEADING_5, heading5)
+ commandManager.add(COMMANDS.PARAGRAPH_HEADING_6, heading6)
+ commandManager.add(COMMANDS.PARAGRAPH_HORIZONTAL_LINE, horizontalLine)
+ commandManager.add(COMMANDS.PARAGRAPH_HTML_BLOCK, htmlBlock)
+ commandManager.add(COMMANDS.PARAGRAPH_LOOSE_LIST_ITEM, looseListItem)
+ commandManager.add(COMMANDS.PARAGRAPH_MATH_FORMULA, mathFormula)
+ commandManager.add(COMMANDS.PARAGRAPH_ORDERED_LIST, orderedList)
+ commandManager.add(COMMANDS.PARAGRAPH_PARAGRAPH, paragraph)
+ commandManager.add(COMMANDS.PARAGRAPH_QUOTE_BLOCK, quoteBlock)
+ commandManager.add(COMMANDS.PARAGRAPH_TABLE, table)
+ commandManager.add(COMMANDS.PARAGRAPH_TASK_LIST, taskList)
+ commandManager.add(COMMANDS.PARAGRAPH_INCREASE_HEADING, increaseHeading)
+}
+
// --- IPC events -------------------------------------------------------------
// NOTE: Don't use static `getMenuItemById` here, instead request the menu by
diff --git a/src/main/menu/actions/view.js b/src/main/menu/actions/view.js
index 8ea4624e..71d4a4d8 100644
--- a/src/main/menu/actions/view.js
+++ b/src/main/menu/actions/view.js
@@ -1,40 +1,95 @@
-import { getMenuItemById } from '../../menu'
+import { ipcMain } from 'electron'
+import { COMMANDS } from '../../commands'
const typewriterModeMenuItemId = 'typewriterModeMenuItem'
const focusModeMenuItemId = 'focusModeMenuItem'
-export const showCommandPalette = win => {
- win.webContents.send('mt::show-command-palette')
-}
-
-export const typeMode = (win, type, item) => {
- if (!win) {
- return
- }
- const { checked } = item
- win.webContents.send('mt::editor-change-view', { type, checked })
-
- if (type === 'sourceCode') {
- const typewriterModeMenuItem = getMenuItemById(typewriterModeMenuItemId)
- const focusModeMenuItem = getMenuItemById(focusModeMenuItemId)
- typewriterModeMenuItem.enabled = !checked
- focusModeMenuItem.enabled = !checked
- }
-}
-
-export const layout = (item, win, type, value) => {
+const toggleTypeMode = (win, type) => {
if (win && win.webContents) {
- win.webContents.send('mt::set-view-layout', { [type]: value || item.checked })
+ win.webContents.send('mt::toggle-view-mode-entry', type)
}
}
+const setLayout = (win, type, value) => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::set-view-layout', { [type]: value })
+ }
+}
+
+const toggleLayout = (win, type) => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::toggle-view-layout-entry', type)
+ }
+}
+
+export const debugToggleDevTools = win => {
+ if (win && global.MARKTEXT_DEBUG) {
+ win.webContents.toggleDevTools()
+ }
+}
+
+export const debugReloadWindow = win => {
+ if (win && global.MARKTEXT_DEBUG) {
+ ipcMain.emit('window-reload-by-id', win.id)
+ }
+}
+
+export const showCommandPalette = win => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::show-command-palette')
+ }
+}
+
+export const toggleFocusMode = win => {
+ toggleTypeMode(win, 'focus')
+}
+
+export const toggleSourceCodeMode = win => {
+ toggleTypeMode(win, 'sourceCode')
+}
+
+export const toggleSidebar = win => {
+ toggleLayout(win, 'showSideBar')
+}
+
+export const toggleTabBar = win => {
+ toggleLayout(win, 'showTabBar')
+}
+
export const showTabBar = win => {
- const tabBarMenuItem = getMenuItemById('tabBarMenuItem')
- if (tabBarMenuItem && !tabBarMenuItem.checked && tabBarMenuItem.click) {
- tabBarMenuItem.click(tabBarMenuItem, win)
+ setLayout(win, 'showTabBar', true)
+}
+
+export const showTableOfContents = win => {
+ setLayout(win, 'rightColumn', 'toc')
+}
+
+export const toggleTypewriterMode = win => {
+ toggleTypeMode(win, 'typewriter')
+}
+
+export const reloadImageCache = win => {
+ if (win && win.webContents) {
+ win.webContents.send('mt::invalidate-image-cache')
}
}
+// --- Commands -------------------------------------------------------------
+
+export const loadViewCommands = commandManager => {
+ commandManager.add(COMMANDS.VIEW_COMMAND_PALETTE, showCommandPalette)
+ commandManager.add(COMMANDS.VIEW_FOCUS_MODE, toggleFocusMode)
+ commandManager.add(COMMANDS.VIEW_FORCE_RELOAD_IMAGES, reloadImageCache)
+ commandManager.add(COMMANDS.VIEW_SOURCE_CODE_MODE, toggleSourceCodeMode)
+ commandManager.add(COMMANDS.VIEW_TOGGLE_SIDEBAR, toggleSidebar)
+ commandManager.add(COMMANDS.VIEW_TOGGLE_TABBAR, toggleTabBar)
+ commandManager.add(COMMANDS.VIEW_TOGGLE_TOC, showTableOfContents)
+ commandManager.add(COMMANDS.VIEW_TYPEWRITER_MODE, toggleTypewriterMode)
+
+ commandManager.add(COMMANDS.VIEW_DEV_RELOAD, debugReloadWindow)
+ commandManager.add(COMMANDS.VIEW_TOGGLE_DEV_TOOLS, debugToggleDevTools)
+}
+
// --- IPC events -------------------------------------------------------------
// NOTE: Don't use static `getMenuItemById` here, instead request the menu by
@@ -46,6 +101,10 @@ export const showTabBar = win => {
* @param {*} changes Array of changed view settings (e.g. [ {showSideBar: true} ]).
*/
export const viewLayoutChanged = (applicationMenu, changes) => {
+ const disableMenuByName = (id, value) => {
+ const menuItem = applicationMenu.getMenuItemById(id)
+ menuItem.enabled = value
+ }
const changeMenuByName = (id, value) => {
const menuItem = applicationMenu.getMenuItemById(id)
menuItem.checked = value
@@ -62,6 +121,8 @@ export const viewLayoutChanged = (applicationMenu, changes) => {
break
case 'sourceCode':
changeMenuByName('sourceCodeModeMenuItem', value)
+ disableMenuByName(focusModeMenuItemId, !value)
+ disableMenuByName(typewriterModeMenuItemId, !value)
break
case 'typewriter':
changeMenuByName(typewriterModeMenuItemId, value)
diff --git a/src/main/menu/actions/window.js b/src/main/menu/actions/window.js
index 6864fb1b..bb573f46 100644
--- a/src/main/menu/actions/window.js
+++ b/src/main/menu/actions/window.js
@@ -1,5 +1,7 @@
import { ipcMain, Menu } from 'electron'
import { isOsx } from '../../config'
+import { COMMANDS } from '../../commands'
+import { zoomIn, zoomOut } from '../../windows/utils'
export const minimizeWindow = win => {
if (win) {
@@ -16,3 +18,19 @@ export const toggleAlwaysOnTop = win => {
ipcMain.emit('window-toggle-always-on-top', win)
}
}
+
+export const toggleFullScreen = win => {
+ if (win) {
+ win.setFullScreen(!win.isFullScreen())
+ }
+}
+
+// --- Commands -------------------------------------------------------------
+
+export const loadWindowCommands = commandManager => {
+ commandManager.add(COMMANDS.WINDOW_MINIMIZE, minimizeWindow)
+ commandManager.add(COMMANDS.WINDOW_TOGGLE_ALWAYS_ON_TOP, toggleAlwaysOnTop)
+ commandManager.add(COMMANDS.WINDOW_TOGGLE_FULL_SCREEN, toggleFullScreen)
+ commandManager.add(COMMANDS.WINDOW_ZOOM_IN, zoomIn)
+ commandManager.add(COMMANDS.WINDOW_ZOOM_OUT, zoomOut)
+}
diff --git a/src/main/menu/index.js b/src/main/menu/index.js
index 344f3f2b..3b640e25 100644
--- a/src/main/menu/index.js
+++ b/src/main/menu/index.js
@@ -4,7 +4,6 @@ import { app, ipcMain, Menu } from 'electron'
import log from 'electron-log'
import { ensureDirSync, isDirectory2, isFile2 } from 'common/filesystem'
import { isLinux, isOsx, isWindows } from '../config'
-import { parseMenu } from '../keyboard/shortcutHandler'
import { updateSidebarMenu } from '../menu/actions/edit'
import { updateFormatMenu } from '../menu/actions/format'
import { updateSelectionMenus } from '../menu/actions/paragraph'
@@ -144,9 +143,9 @@ class AppMenu {
addEditorMenu (window, options = {}) {
const isSourceMode = !!options.sourceCodeModeEnabled
const { windowMenus } = this
- windowMenus.set(window.id, this._buildEditorMenu(true))
+ windowMenus.set(window.id, this._buildEditorMenu())
- const { menu, shortcutMap } = windowMenus.get(window.id)
+ const { menu } = windowMenus.get(window.id)
// Set source-code editor if preferred.
const sourceCodeModeMenuItem = menu.getMenuItemById('sourceCodeModeMenuItem')
@@ -158,7 +157,19 @@ class AppMenu {
typewriterModeMenuItem.enabled = false
focusModeMenuItem.enabled = false
}
- this._keybindings.registerKeyHandlers(window, shortcutMap)
+
+ const { _keybindings } = this
+ _keybindings.registerEditorKeyHandlers(window)
+
+ if (isWindows) {
+ // WORKAROUND: Window close event isn't triggered on Windows if `setIgnoreMenuShortcuts(true)` is used (Electron#32674).
+ // NB: Remove this immediately if upstream is fixed because the event may be emitted twice.
+ _keybindings.registerAccelerator(window, 'Alt+F4', win => {
+ if (win && !win.isDestroyed()) {
+ win.close()
+ }
+ })
+ }
}
/**
@@ -233,7 +244,7 @@ class AppMenu {
const { menu: oldMenu, type } = value
if (type !== MenuType.EDITOR) return
- const { menu: newMenu } = this._buildEditorMenu(false, recentUsedDocuments)
+ const { menu: newMenu } = this._buildEditorMenu(recentUsedDocuments)
// all other menu items are set automatically
updateMenuItem(oldMenu, newMenu, 'sourceCodeModeMenuItem')
@@ -325,152 +336,14 @@ class AppMenu {
})
}
- /**
- * Append misc shortcuts the the given shortcut map.
- *
- * @param {*} lineEnding The shortcut map.
- */
- _appendMiscShortcuts = shortcutMap => {
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.cycle-forward'),
- click: (menuItem, win) => {
- win.webContents.send('mt::tabs-cycle-right')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.cycle-backward'),
- click: (menuItem, win) => {
- win.webContents.send('mt::tabs-cycle-left')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-left'),
- click: (menuItem, win) => {
- win.webContents.send('mt::tabs-cycle-left')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-right'),
- click: (menuItem, win) => {
- win.webContents.send('mt::tabs-cycle-right')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-first'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-first-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-second'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-second-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-third'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-third-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-fourth'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-fourth-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-fifth'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-fifth-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-sixth'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-sixth-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-seventh'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-seventh-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-eighth'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-eighth-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-ninth'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-ninth-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('tabs.switch-to-tenth'),
- click: (menuItem, win) => {
- win.webContents.send('mt::switch-tenth-tab')
- },
- id: null
- })
- shortcutMap.push({
- accelerator: this._keybindings.getAccelerator('file.quick-open'),
- click: (menuItem, win) => {
- win.webContents.send('mt::execute-command-by-id', 'file.quick-open')
- },
- id: null
- })
-
- if (isWindows) {
- // WORKAROUND: Window close event isn't triggered on Windows if `setIgnoreMenuShortcuts(true)` is used (Electron#32674).
- // NB: Remove this immediately if upstream is fixed because the event may be emitted twice.
- shortcutMap.push({
- accelerator: 'Alt+F4',
- click: (menuItem, win) => {
- if (win && !win.isDestroyed()) {
- win.close()
- }
- },
- id: null
- })
- }
- }
-
- _buildEditorMenu (createShortcutMap, recentUsedDocuments = null) {
+ _buildEditorMenu (recentUsedDocuments = null) {
if (!recentUsedDocuments) {
recentUsedDocuments = this.getRecentlyUsedDocuments()
}
const menuTemplate = configureMenu(this._keybindings, this._preferences, recentUsedDocuments)
const menu = Menu.buildFromTemplate(menuTemplate)
-
- let shortcutMap = null
- if (createShortcutMap) {
- shortcutMap = parseMenu(menuTemplate)
- this._appendMiscShortcuts(shortcutMap)
- }
-
- return {
- shortcutMap,
- menu,
- type: MenuType.EDITOR
- }
+ return { menu, type: MenuType.EDITOR }
}
_buildSettingMenu () {
diff --git a/src/main/menu/templates/dock.js b/src/main/menu/templates/dock.js
index 9f000c18..d2ca61f9 100644
--- a/src/main/menu/templates/dock.js
+++ b/src/main/menu/templates/dock.js
@@ -4,7 +4,11 @@ import * as actions from '../actions/file'
const dockMenu = Menu.buildFromTemplate([{
label: 'Open...',
click (menuItem, browserWindow) {
- actions.openFile(browserWindow)
+ if (browserWindow) {
+ actions.openFile(browserWindow)
+ } else {
+ actions.newEditorWindow()
+ }
}
}, {
label: 'Clear Recent',
diff --git a/src/main/menu/templates/edit.js b/src/main/menu/templates/edit.js
index 93cf5ddd..332a703c 100755
--- a/src/main/menu/templates/edit.js
+++ b/src/main/menu/templates/edit.js
@@ -1,38 +1,39 @@
import * as actions from '../actions/edit'
import { isOsx } from '../../config'
+import { COMMANDS } from '../../commands'
export default function (keybindings) {
return {
label: '&Edit',
submenu: [{
label: 'Undo',
- accelerator: keybindings.getAccelerator('edit.undo'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_UNDO),
click: (menuItem, browserWindow) => {
- actions.edit(browserWindow, 'undo')
+ actions.editorUndo(browserWindow)
}
}, {
label: 'Redo',
- accelerator: keybindings.getAccelerator('edit.redo'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_REDO),
click: (menuItem, browserWindow) => {
- actions.edit(browserWindow, 'redo')
+ actions.editorRedo(browserWindow)
}
}, {
type: 'separator'
}, {
label: 'Cut',
- accelerator: keybindings.getAccelerator('edit.cut'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_CUT),
click (menuItem, browserWindow) {
actions.nativeCut(browserWindow)
}
}, {
label: 'Copy',
- accelerator: keybindings.getAccelerator('edit.copy'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_COPY),
click (menuItem, browserWindow) {
actions.nativeCopy(browserWindow)
}
}, {
label: 'Paste',
- accelerator: keybindings.getAccelerator('edit.paste'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_PASTE),
click (menuItem, browserWindow) {
actions.nativePaste(browserWindow)
}
@@ -40,83 +41,83 @@ export default function (keybindings) {
type: 'separator'
}, {
label: 'Copy as Markdown',
- accelerator: keybindings.getAccelerator('edit.copy-as-markdown'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_COPY_AS_MARKDOWN),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'copyAsMarkdown')
+ actions.editorCopyAsMarkdown(browserWindow)
}
}, {
label: 'Copy as HTML',
- accelerator: keybindings.getAccelerator('edit.copy-as-html'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_COPY_AS_HTML),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'copyAsHtml')
+ actions.editorCopyAsHtml(browserWindow)
}
}, {
label: 'Paste as Plain Text',
- accelerator: keybindings.getAccelerator('edit.paste-as-plaintext'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_PASTE_AS_PLAINTEXT),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'pasteAsPlainText')
+ actions.editorPasteAsPlainText(browserWindow)
}
}, {
type: 'separator'
}, {
label: 'Select All',
- accelerator: keybindings.getAccelerator('edit.select-all'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_SELECT_ALL),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'selectAll')
+ actions.editorSelectAll(browserWindow)
}
}, {
type: 'separator'
}, {
label: 'Duplicate',
- accelerator: keybindings.getAccelerator('edit.duplicate'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_DUPLICATE),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'duplicate')
+ actions.editorDuplicate(browserWindow)
}
}, {
label: 'Create Paragraph',
- accelerator: keybindings.getAccelerator('edit.create-paragraph'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_CREATE_PARAGRAPH),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'createParagraph')
+ actions.editorCreateParagraph(browserWindow)
}
}, {
label: 'Delete Paragraph',
- accelerator: keybindings.getAccelerator('edit.delete-paragraph'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_DELETE_PARAGRAPH),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'deleteParagraph')
+ actions.editorDeleteParagraph(browserWindow)
}
}, {
type: 'separator'
}, {
label: 'Find',
- accelerator: keybindings.getAccelerator('edit.find'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_FIND),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'find')
+ actions.editorFind(browserWindow)
}
}, {
label: 'Find Next',
- accelerator: keybindings.getAccelerator('edit.find-next'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_FIND_NEXT),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'findNext')
+ actions.editorFindNext(browserWindow)
}
}, {
label: 'Find Previous',
- accelerator: keybindings.getAccelerator('edit.find-previous'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_FIND_PREVIOUS),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'findPrev')
+ actions.editorFindPrevious(browserWindow)
}
}, {
label: 'Replace',
- accelerator: keybindings.getAccelerator('edit.replace'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_REPLACE),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'replace')
+ actions.editorReplace(browserWindow)
}
}, {
type: 'separator'
}, {
label: 'Find in Folder',
- accelerator: keybindings.getAccelerator('edit.find-in-folder'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_FIND_IN_FOLDER),
click (menuItem, browserWindow) {
- actions.edit(browserWindow, 'findInFolder')
+ actions.findInFolder(browserWindow)
}
}, {
type: 'separator'
@@ -124,7 +125,7 @@ export default function (keybindings) {
label: 'Screenshot',
id: 'screenshot',
visible: isOsx,
- accelerator: keybindings.getAccelerator('edit.screenshot'),
+ accelerator: keybindings.getAccelerator(COMMANDS.EDIT_SCREENSHOT),
click (menuItem, browserWindow) {
actions.screenshot(browserWindow)
}
diff --git a/src/main/menu/templates/file.js b/src/main/menu/templates/file.js
index cbdec764..b62ac0c3 100755
--- a/src/main/menu/templates/file.js
+++ b/src/main/menu/templates/file.js
@@ -1,7 +1,6 @@
import { app } from 'electron'
import * as actions from '../actions/file'
import { userSetting } from '../actions/marktext'
-import { showTabBar } from '../actions/view'
import { isOsx } from '../../config'
export default function (keybindings, userPreference, recentlyUsedFiles) {
@@ -13,11 +12,10 @@ export default function (keybindings, userPreference, recentlyUsedFiles) {
accelerator: keybindings.getAccelerator('file.new-tab'),
click (menuItem, browserWindow) {
actions.newBlankTab(browserWindow)
- showTabBar(browserWindow)
}
}, {
label: 'New Window',
- accelerator: keybindings.getAccelerator('file.new-file'),
+ accelerator: keybindings.getAccelerator('file.new-window'),
click (menuItem, browserWindow) {
actions.newEditorWindow()
}
@@ -77,8 +75,6 @@ export default function (keybindings, userPreference, recentlyUsedFiles) {
fileMenu.submenu.push({
type: 'separator'
- }, {
- type: 'separator'
}, {
label: 'Save',
accelerator: keybindings.getAccelerator('file.save'),
@@ -139,7 +135,7 @@ export default function (keybindings, userPreference, recentlyUsedFiles) {
label: 'Print',
accelerator: keybindings.getAccelerator('file.print'),
click (menuItem, browserWindow) {
- actions.print(browserWindow)
+ actions.printDocument(browserWindow)
}
}, {
type: 'separator',
diff --git a/src/main/menu/templates/format.js b/src/main/menu/templates/format.js
index d3d9ea80..58aa72ef 100644
--- a/src/main/menu/templates/format.js
+++ b/src/main/menu/templates/format.js
@@ -9,24 +9,24 @@ export default function (keybindings) {
label: 'Bold',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.strong'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'strong')
+ click (menuItem, focusedWindow) {
+ actions.strong(focusedWindow)
}
}, {
id: 'emphasisMenuItem',
label: 'Italic',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.emphasis'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'em')
+ click (menuItem, focusedWindow) {
+ actions.emphasis(focusedWindow)
}
}, {
id: 'underlineMenuItem',
label: 'Underline',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.underline'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'u')
+ click (menuItem, focusedWindow) {
+ actions.underline(focusedWindow)
}
}, {
type: 'separator'
@@ -35,24 +35,24 @@ export default function (keybindings) {
label: 'Superscript',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.superscript'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'sup')
+ click (menuItem, focusedWindow) {
+ actions.superscript(focusedWindow)
}
}, {
id: 'subscriptMenuItem',
label: 'Subscript',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.subscript'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'sub')
+ click (menuItem, focusedWindow) {
+ actions.subscript(focusedWindow)
}
}, {
id: 'highlightMenuItem',
label: 'Highlight',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.highlight'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'mark')
+ click (menuItem, focusedWindow) {
+ actions.highlight(focusedWindow)
}
}, {
type: 'separator'
@@ -61,16 +61,16 @@ export default function (keybindings) {
label: 'Inline Code',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.inline-code'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'inline_code')
+ click (menuItem, focusedWindow) {
+ actions.inlineCode(focusedWindow)
}
}, {
id: 'inlineMathMenuItem',
label: 'Inline Math',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.inline-math'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'inline_math')
+ click (menuItem, focusedWindow) {
+ actions.inlineMath(focusedWindow)
}
}, {
type: 'separator'
@@ -79,32 +79,32 @@ export default function (keybindings) {
label: 'Strikethrough',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.strike'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'del')
+ click (menuItem, focusedWindow) {
+ actions.strikethrough(focusedWindow)
}
}, {
id: 'hyperlinkMenuItem',
label: 'Hyperlink',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.hyperlink'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'link')
+ click (menuItem, focusedWindow) {
+ actions.hyperlink(focusedWindow)
}
}, {
id: 'imageMenuItem',
label: 'Image',
type: 'checkbox',
accelerator: keybindings.getAccelerator('format.image'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'image')
+ click (menuItem, focusedWindow) {
+ actions.image(focusedWindow)
}
}, {
type: 'separator'
}, {
label: 'Clear Formatting',
accelerator: keybindings.getAccelerator('format.clear-format'),
- click (menuItem, browserWindow) {
- actions.format(browserWindow, 'clear')
+ click (menuItem, focusedWindow) {
+ actions.clearFormat(focusedWindow)
}
}]
}
diff --git a/src/main/menu/templates/marktext.js b/src/main/menu/templates/marktext.js
index 696c2da6..92678189 100755
--- a/src/main/menu/templates/marktext.js
+++ b/src/main/menu/templates/marktext.js
@@ -1,4 +1,4 @@
-import { app, Menu } from 'electron'
+import { app } from 'electron'
import { showAboutDialog } from '../actions/help'
import * as actions from '../actions/marktext'
@@ -9,13 +9,13 @@ export default function (keybindings) {
label: 'MarkText',
submenu: [{
label: 'About MarkText',
- click (menuItem, browserWindow) {
- showAboutDialog(browserWindow)
+ click (menuItem, focusedWindow) {
+ showAboutDialog(focusedWindow)
}
}, {
label: 'Check for updates...',
- click (menuItem, browserWindow) {
- actions.checkUpdates(browserWindow)
+ click (menuItem, focusedWindow) {
+ actions.checkUpdates(focusedWindow)
}
}, {
label: 'Preferences',
@@ -35,18 +35,18 @@ export default function (keybindings) {
label: 'Hide MarkText',
accelerator: keybindings.getAccelerator('mt.hide'),
click () {
- Menu.sendActionToFirstResponder('hide:')
+ actions.osxHide()
}
}, {
label: 'Hide Others',
accelerator: keybindings.getAccelerator('mt.hide-others'),
click () {
- Menu.sendActionToFirstResponder('hideOtherApplications:')
+ actions.osxHideAll()
}
}, {
label: 'Show All',
click () {
- Menu.sendActionToFirstResponder('unhideAllApplications:')
+ actions.osxShowAll()
}
}, {
type: 'separator'
diff --git a/src/main/menu/templates/paragraph.js b/src/main/menu/templates/paragraph.js
index b3a3e54f..37e5a864 100644
--- a/src/main/menu/templates/paragraph.js
+++ b/src/main/menu/templates/paragraph.js
@@ -9,48 +9,48 @@ export default function (keybindings) {
label: 'Heading 1',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.heading-1'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'heading 1')
+ click (menuItem, focusedWindow) {
+ actions.heading1(focusedWindow)
}
}, {
id: 'heading2MenuItem',
label: 'Heading 2',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.heading-2'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'heading 2')
+ click (menuItem, focusedWindow) {
+ actions.heading2(focusedWindow)
}
}, {
id: 'heading3MenuItem',
label: 'Heading 3',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.heading-3'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'heading 3')
+ click (menuItem, focusedWindow) {
+ actions.heading3(focusedWindow)
}
}, {
id: 'heading4MenuItem',
label: 'Heading 4',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.heading-4'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'heading 4')
+ click (menuItem, focusedWindow) {
+ actions.heading4(focusedWindow)
}
}, {
id: 'heading5MenuItem',
label: 'Heading 5',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.heading-5'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'heading 5')
+ click (menuItem, focusedWindow) {
+ actions.heading5(focusedWindow)
}
}, {
id: 'heading6MenuItem',
label: 'Heading 6',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.heading-6'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'heading 6')
+ click (menuItem, focusedWindow) {
+ actions.heading6(focusedWindow)
}
}, {
type: 'separator'
@@ -58,15 +58,15 @@ export default function (keybindings) {
id: 'upgradeHeadingMenuItem',
label: 'Promote Heading',
accelerator: keybindings.getAccelerator('paragraph.upgrade-heading'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'upgrade heading')
+ click (menuItem, focusedWindow) {
+ actions.increaseHeading(focusedWindow)
}
}, {
id: 'degradeHeadingMenuItem',
label: 'Demote Heading',
accelerator: keybindings.getAccelerator('paragraph.degrade-heading'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'degrade heading')
+ click (menuItem, focusedWindow) {
+ actions.degradeHeading(focusedWindow)
}
}, {
type: 'separator'
@@ -75,40 +75,40 @@ export default function (keybindings) {
label: 'Table',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.table'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'table')
+ click (menuItem, focusedWindow) {
+ actions.table(focusedWindow)
}
}, {
id: 'codeFencesMenuItem',
label: 'Code Fences',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.code-fence'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'pre')
+ click (menuItem, focusedWindow) {
+ actions.codeFence(focusedWindow)
}
}, {
id: 'quoteBlockMenuItem',
label: 'Quote Block',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.quote-block'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'blockquote')
+ click (menuItem, focusedWindow) {
+ actions.quoteBlock(focusedWindow)
}
}, {
id: 'mathBlockMenuItem',
label: 'Math Block',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.math-formula'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'mathblock')
+ click (menuItem, focusedWindow) {
+ actions.mathFormula(focusedWindow)
}
}, {
id: 'htmlBlockMenuItem',
label: 'Html Block',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.html-block'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'html')
+ click (menuItem, focusedWindow) {
+ actions.htmlBlock(focusedWindow)
}
}, {
type: 'separator'
@@ -117,24 +117,24 @@ export default function (keybindings) {
label: 'Ordered List',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.order-list'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'ol-order')
+ click (menuItem, focusedWindow) {
+ actions.orderedList(focusedWindow)
}
}, {
id: 'bulletListMenuItem',
label: 'Bullet List',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.bullet-list'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'ul-bullet')
+ click (menuItem, focusedWindow) {
+ actions.bulletList(focusedWindow)
}
}, {
id: 'taskListMenuItem',
label: 'Task List',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.task-list'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'ul-task')
+ click (menuItem, focusedWindow) {
+ actions.taskList(focusedWindow)
}
}, {
type: 'separator'
@@ -143,8 +143,8 @@ export default function (keybindings) {
label: 'Loose List Item',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.loose-list-item'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'loose-list-item')
+ click (menuItem, focusedWindow) {
+ actions.looseListItem(focusedWindow)
}
}, {
type: 'separator'
@@ -153,24 +153,24 @@ export default function (keybindings) {
label: 'Paragraph',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.paragraph'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'paragraph')
+ click (menuItem, focusedWindow) {
+ actions.paragraph(focusedWindow)
}
}, {
id: 'horizontalLineMenuItem',
label: 'Horizontal Rule',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.horizontal-line'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'hr')
+ click (menuItem, focusedWindow) {
+ actions.horizontalLine(focusedWindow)
}
}, {
id: 'frontMatterMenuItem',
label: 'Front Matter',
type: 'checkbox',
accelerator: keybindings.getAccelerator('paragraph.front-matter'),
- click (menuItem, browserWindow) {
- actions.paragraph(browserWindow, 'front-matter')
+ click (menuItem, focusedWindow) {
+ actions.frontMatter(focusedWindow)
}
}]
}
diff --git a/src/main/menu/templates/view.js b/src/main/menu/templates/view.js
index a8d5f09c..b506d5d8 100755
--- a/src/main/menu/templates/view.js
+++ b/src/main/menu/templates/view.js
@@ -1,4 +1,3 @@
-import { ipcMain } from 'electron'
import * as actions from '../actions/view'
export default function (keybindings) {
@@ -7,8 +6,8 @@ export default function (keybindings) {
submenu: [{
label: 'Command Palette...',
accelerator: keybindings.getAccelerator('view.command-palette'),
- click (menuItem, browserWindow) {
- actions.showCommandPalette(browserWindow)
+ click (menuItem, focusedWindow) {
+ actions.showCommandPalette(focusedWindow)
}
}, {
type: 'separator'
@@ -18,12 +17,8 @@ export default function (keybindings) {
accelerator: keybindings.getAccelerator('view.source-code-mode'),
type: 'checkbox',
checked: false,
- click (item, browserWindow, event) {
- // if we call this function, the checked state is not set
- if (!event) {
- item.checked = !item.checked
- }
- actions.typeMode(browserWindow, 'sourceCode', item)
+ click (item, focusedWindow) {
+ actions.toggleSourceCodeMode(focusedWindow)
}
}, {
id: 'typewriterModeMenuItem',
@@ -31,12 +26,8 @@ export default function (keybindings) {
accelerator: keybindings.getAccelerator('view.typewriter-mode'),
type: 'checkbox',
checked: false,
- click (item, browserWindow, event) {
- // if we call this function, the checked state is not set
- if (!event) {
- item.checked = !item.checked
- }
- actions.typeMode(browserWindow, 'typewriter', item)
+ click (item, focusedWindow) {
+ actions.toggleTypewriterMode(focusedWindow)
}
}, {
id: 'focusModeMenuItem',
@@ -44,12 +35,8 @@ export default function (keybindings) {
accelerator: keybindings.getAccelerator('view.focus-mode'),
type: 'checkbox',
checked: false,
- click (item, browserWindow, event) {
- // if we call this function, the checked state is not set
- if (!event) {
- item.checked = !item.checked
- }
- actions.typeMode(browserWindow, 'focus', item)
+ click (item, focusedWindow) {
+ actions.toggleFocusMode(focusedWindow)
}
}, {
type: 'separator'
@@ -59,13 +46,8 @@ export default function (keybindings) {
accelerator: keybindings.getAccelerator('view.toggle-sidebar'),
type: 'checkbox',
checked: false,
- click (item, browserWindow, event) {
- // if we call this function, the checked state is not set
- if (!event) {
- item.checked = !item.checked
- }
-
- actions.layout(item, browserWindow, 'showSideBar')
+ click (item, focusedWindow) {
+ actions.toggleSidebar(focusedWindow)
}
}, {
label: 'Show Tab Bar',
@@ -73,51 +55,41 @@ export default function (keybindings) {
accelerator: keybindings.getAccelerator('view.toggle-tabbar'),
type: 'checkbox',
checked: false,
- click (item, browserWindow, event) {
- // if we call this function, the checked state is not set
- if (!event) {
- item.checked = !item.checked
- }
-
- actions.layout(item, browserWindow, 'showTabBar')
+ click (item, focusedWindow) {
+ actions.toggleTabBar(focusedWindow)
}
}, {
label: 'Toggle Table of Contents',
id: 'tocMenuItem',
accelerator: keybindings.getAccelerator('view.toggle-toc'),
- click (_, browserWindow) {
- actions.layout(null, browserWindow, 'rightColumn', 'toc')
+ click (_, focusedWindow) {
+ actions.showTableOfContents(focusedWindow)
}
}, {
label: 'Reload Images',
accelerator: keybindings.getAccelerator('view.reload-images'),
click (item, focusedWindow) {
- if (focusedWindow) {
- focusedWindow.webContents.send('mt::invalidate-image-cache', {})
- }
+ actions.reloadImageCache(focusedWindow)
}
- }, {
- type: 'separator'
}]
}
if (global.MARKTEXT_DEBUG) {
+ viewMenu.submenu.push({
+ type: 'separator'
+ })
viewMenu.submenu.push({
label: 'Show Developer Tools',
accelerator: keybindings.getAccelerator('view.toggle-dev-tools'),
- click (item, focusedWindow) {
- if (focusedWindow) {
- focusedWindow.webContents.toggleDevTools()
- }
+ click (item, win) {
+ actions.debugToggleDevTools(win)
}
})
viewMenu.submenu.push({
label: 'Reload window',
accelerator: keybindings.getAccelerator('view.dev-reload'),
click (item, focusedWindow) {
- if (focusedWindow) {
- ipcMain.emit('window-reload-by-id', focusedWindow.id)
- }
+ actions.debugReloadWindow(focusedWindow)
}
})
}
diff --git a/src/main/menu/templates/window.js b/src/main/menu/templates/window.js
index 2e4e8d52..f37fd953 100755
--- a/src/main/menu/templates/window.js
+++ b/src/main/menu/templates/window.js
@@ -1,5 +1,5 @@
import { Menu } from 'electron'
-import { minimizeWindow, toggleAlwaysOnTop } from '../actions/window'
+import { minimizeWindow, toggleAlwaysOnTop, toggleFullScreen } from '../actions/window'
import { zoomIn, zoomOut } from '../../windows/utils'
import { isOsx } from '../../config'
@@ -40,9 +40,9 @@ export default function (keybindings) {
}, {
label: 'Show in Full Screen',
accelerator: keybindings.getAccelerator('window.toggle-full-screen'),
- click (item, focusedWindow) {
- if (focusedWindow) {
- focusedWindow.setFullScreen(!focusedWindow.isFullScreen())
+ click (item, browserWindow) {
+ if (browserWindow) {
+ toggleFullScreen(browserWindow)
}
}
}]
diff --git a/src/main/preferences/schema.json b/src/main/preferences/schema.json
index 6cbf587f..1cf93be6 100644
--- a/src/main/preferences/schema.json
+++ b/src/main/preferences/schema.json
@@ -346,6 +346,7 @@
},
"spellcheckerLanguage": {
"description": "Spelling--The spell checker language",
+ "type": "string",
"pattern": "^[a-z]{2,3}(?:[-](?:[0-9]|[a-zA-Z]){2,}){0,2}$",
"default": "en-US"
},
diff --git a/src/renderer/commands/descriptions.js b/src/renderer/commands/descriptions.js
index bb549be0..4a58e0d3 100644
--- a/src/renderer/commands/descriptions.js
+++ b/src/renderer/commands/descriptions.js
@@ -5,7 +5,7 @@ const commandDescriptions = Object.freeze({
'mt.hide': 'MarkText: Hide MarkText',
'mt.hide-others': 'MarkText: Hide Others',
- 'file.new-file': 'File: New Window',
+ 'file.new-window': 'File: New Window',
'file.new-tab': 'File: New Tab',
'file.open-file': 'File: Open file',
'file.open-folder': 'File: Open Folder',
@@ -102,7 +102,7 @@ const commandDescriptions = Object.freeze({
// # Menu descriptions but not available as command
// #
- 'view.reload-images': 'View: Clear cache and reload images',
+ 'view.reload-images': 'View: Force reload images',
// ============================================
// # Additional command descriptions
diff --git a/src/renderer/commands/index.js b/src/renderer/commands/index.js
index b8669441..6d064c6c 100644
--- a/src/renderer/commands/index.js
+++ b/src/renderer/commands/index.js
@@ -43,7 +43,7 @@ const commands = [
ipcRenderer.emit('mt::new-untitled-tab', null)
}
}, {
- id: 'file.new-file',
+ id: 'file.new-window',
execute: async () => {
ipcRenderer.send('mt::cmd-new-editor-window')
}
@@ -581,12 +581,12 @@ const commands = [
}, {
id: 'view.toggle-sidebar',
execute: async () => {
- bus.$emit('view:toggle-view-layout-entry', 'showSideBar')
+ bus.$emit('view:toggle-layout-entry', 'showSideBar')
}
}, {
id: 'view.toggle-tabbar',
execute: async () => {
- bus.$emit('view:toggle-view-layout-entry', 'showTabBar')
+ bus.$emit('view:toggle-layout-entry', 'showTabBar')
}
},
diff --git a/src/renderer/pages/app.vue b/src/renderer/pages/app.vue
index a680a7ad..f85843a9 100644
--- a/src/renderer/pages/app.vue
+++ b/src/renderer/pages/app.vue
@@ -125,7 +125,6 @@ export default {
dispatch('LISTEN_FOR_TWEET')
// module: layout
dispatch('LISTEN_FOR_LAYOUT')
- dispatch('LISTEN_FOR_REQUEST_LAYOUT')
// module: listenForMain
dispatch('LISTEN_FOR_EDIT')
dispatch('LISTEN_FOR_VIEW')
diff --git a/src/renderer/store/editor.js b/src/renderer/store/editor.js
index 6726503f..bc3005a9 100644
--- a/src/renderer/store/editor.js
+++ b/src/renderer/store/editor.js
@@ -646,7 +646,7 @@ const actions = {
showSideBar: !!sideBarVisibility,
showTabBar: !!tabBarVisibility
})
- dispatch('SET_LAYOUT_MENU_ITEM')
+ dispatch('DISPATCH_LAYOUT_MENU_ITEMS')
commit('SET_MODE', {
type: 'sourceCode',
@@ -701,35 +701,8 @@ const actions = {
},
LISTEN_FOR_SWITCH_TABS ({ commit, state, dispatch }) {
- ipcRenderer.on('mt::switch-first-tab', e => {
- dispatch('SWITCH_TABS', 1)
- })
- ipcRenderer.on('mt::switch-second-tab', e => {
- dispatch('SWITCH_TABS', 2)
- })
- ipcRenderer.on('mt::switch-third-tab', e => {
- dispatch('SWITCH_TABS', 3)
- })
- ipcRenderer.on('mt::switch-fourth-tab', e => {
- dispatch('SWITCH_TABS', 4)
- })
- ipcRenderer.on('mt::switch-fifth-tab', e => {
- dispatch('SWITCH_TABS', 5)
- })
- ipcRenderer.on('mt::switch-sixth-tab', e => {
- dispatch('SWITCH_TABS', 6)
- })
- ipcRenderer.on('mt::switch-seventh-tab', e => {
- dispatch('SWITCH_TABS', 7)
- })
- ipcRenderer.on('mt::switch-eighth-tab', e => {
- dispatch('SWITCH_TABS', 8)
- })
- ipcRenderer.on('mt::switch-ninth-tab', e => {
- dispatch('SWITCH_TABS', 9)
- })
- ipcRenderer.on('mt::switch-tenth-tab', e => {
- dispatch('SWITCH_TABS', 10)
+ ipcRenderer.on('mt::switch-tab-by-index', (event, index) => {
+ dispatch('SWITCH_TAB_BY_INDEX', index)
})
},
@@ -796,29 +769,30 @@ const actions = {
console.error(`CYCLE_TABS: Cannot find next tab (index="${nextTabIndex}").`)
return
}
+
commit('SET_CURRENT_FILE', nextTab)
dispatch('UPDATE_LINE_ENDING_MENU')
},
- // switch tabs with Alt+#num.
- SWITCH_TABS ({ commit, dispatch, state }, num) {
+ SWITCH_TAB_BY_INDEX ({ commit, dispatch, state }, nextTabIndex) {
const { tabs, currentFile } = state
- if (tabs.length <= 1 || num > tabs.length) {
+ if (nextTabIndex < 0 || nextTabIndex >= tabs.length) {
+ console.warn('Invalid tab index:', nextTabIndex)
return
}
const currentIndex = tabs.findIndex(t => t.id === currentFile.id)
if (currentIndex === -1) {
- console.error('CYCLE_TABS: Cannot find current tab index.')
+ console.error('Cannot find current tab index.')
return
}
- const nextTabIndex = num - 1
const nextTab = tabs[nextTabIndex]
if (!nextTab || !nextTab.id) {
- console.error(`CYCLE_TABS: Cannot find next tab (index="${nextTabIndex}").`)
+ console.error(`Cannot find tab by index="${nextTabIndex}".`)
return
}
+
commit('SET_CURRENT_FILE', nextTab)
dispatch('UPDATE_LINE_ENDING_MENU')
},
@@ -916,7 +890,7 @@ const actions = {
const { tabs } = state
if (always || tabs.length === 1) {
commit('SET_LAYOUT', { showTabBar: true })
- dispatch('SET_LAYOUT_MENU_ITEM')
+ dispatch('DISPATCH_LAYOUT_MENU_ITEMS')
}
},
@@ -1231,7 +1205,7 @@ const actions = {
},
LISTEN_FOR_RELOAD_IMAGES () {
- ipcRenderer.on('mt::invalidate-image-cache', (e) => {
+ ipcRenderer.on('mt::invalidate-image-cache', () => {
bus.$emit('invalidate-image-cache')
})
}
diff --git a/src/renderer/store/layout.js b/src/renderer/store/layout.js
index 8bf95131..b45f83bc 100644
--- a/src/renderer/store/layout.js
+++ b/src/renderer/store/layout.js
@@ -33,7 +33,7 @@ const mutations = {
}
const actions = {
- LISTEN_FOR_LAYOUT ({ state, commit }) {
+ LISTEN_FOR_LAYOUT ({ state, commit, dispatch }) {
ipcRenderer.on('mt::set-view-layout', (e, layout) => {
if (layout.rightColumn) {
commit('SET_LAYOUT', {
@@ -44,26 +44,27 @@ const actions = {
} else {
commit('SET_LAYOUT', layout)
}
+ dispatch('DISPATCH_LAYOUT_MENU_ITEMS')
})
- bus.$on('view:toggle-view-layout-entry', entryName => {
+ ipcRenderer.on('mt::toggle-view-layout-entry', (event, entryName) => {
+ commit('TOGGLE_LAYOUT_ENTRY', entryName)
+ dispatch('DISPATCH_LAYOUT_MENU_ITEMS')
+ })
+
+ bus.$on('view:toggle-layout-entry', entryName => {
commit('TOGGLE_LAYOUT_ENTRY', entryName)
- const item = {}
- item[entryName] = state[entryName]
const { windowId } = global.marktext.env
- ipcRenderer.send('mt::view-layout-changed', windowId, item)
+ ipcRenderer.send('mt::view-layout-changed', windowId, { [entryName]: state[entryName] })
})
},
- LISTEN_FOR_REQUEST_LAYOUT ({ dispatch }) {
- ipcRenderer.on('mt::request-for-view-layout', () => {
- dispatch('SET_LAYOUT_MENU_ITEM')
- })
- },
- SET_LAYOUT_MENU_ITEM ({ state }) {
+
+ DISPATCH_LAYOUT_MENU_ITEMS ({ state }) {
const { windowId } = global.marktext.env
const { showTabBar, showSideBar } = state
ipcRenderer.send('mt::view-layout-changed', windowId, { showTabBar, showSideBar })
},
+
CHANGE_SIDE_BAR_WIDTH ({ commit }, width) {
commit('SET_SIDE_BAR_WIDTH', width)
}
diff --git a/src/renderer/store/listenForMain.js b/src/renderer/store/listenForMain.js
index aa4100cd..82820f13 100644
--- a/src/renderer/store/listenForMain.js
+++ b/src/renderer/store/listenForMain.js
@@ -1,7 +1,6 @@
import { ipcRenderer } from 'electron'
import bus from '../bus'
-// messages from main process, and do not change the state
const state = {}
const getters = {}
@@ -21,15 +20,6 @@ const actions = {
})
},
- LISTEN_FOR_VIEW ({ commit }) {
- ipcRenderer.on('mt::editor-change-view', (e, data) => {
- commit('SET_MODE', data)
- })
- ipcRenderer.on('mt::show-command-palette', () => {
- bus.$emit('show-command-palette')
- })
- },
-
LISTEN_FOR_SHOW_DIALOG ({ commit }) {
ipcRenderer.on('mt::about-dialog', e => {
bus.$emit('aboutDialog')
diff --git a/src/renderer/store/preferences.js b/src/renderer/store/preferences.js
index c83d7bcb..209b0aa8 100644
--- a/src/renderer/store/preferences.js
+++ b/src/renderer/store/preferences.js
@@ -117,7 +117,7 @@ const mutations = {
}
const actions = {
- ASK_FOR_USER_PREFERENCE ({ commit, state, rootState }) {
+ ASK_FOR_USER_PREFERENCE ({ commit }) {
ipcRenderer.send('mt::ask-for-user-preference')
ipcRenderer.send('mt::ask-for-user-data')
@@ -143,15 +143,27 @@ const actions = {
ipcRenderer.send('mt::select-default-directory-to-open')
},
+ LISTEN_FOR_VIEW ({ commit, dispatch }) {
+ ipcRenderer.on('mt::show-command-palette', () => {
+ bus.$emit('show-command-palette')
+ })
+ ipcRenderer.on('mt::toggle-view-mode-entry', (event, entryName) => {
+ commit('TOGGLE_VIEW_MODE', entryName)
+ dispatch('DISPATCH_EDITOR_VIEW_STATE', { [entryName]: state[entryName] })
+ })
+ },
+
// Toggle a view option and notify main process to toggle menu item.
- LISTEN_TOGGLE_VIEW ({ commit, state }) {
+ LISTEN_TOGGLE_VIEW ({ commit, dispatch, state }) {
bus.$on('view:toggle-view-entry', entryName => {
commit('TOGGLE_VIEW_MODE', entryName)
- const item = {}
- item[entryName] = state[entryName]
- const { windowId } = global.marktext.env
- ipcRenderer.send('mt::view-layout-changed', windowId, item)
+ dispatch('DISPATCH_EDITOR_VIEW_STATE', { [entryName]: state[entryName] })
})
+ },
+
+ DISPATCH_EDITOR_VIEW_STATE (_, viewState) {
+ const { windowId } = global.marktext.env
+ ipcRenderer.send('mt::view-layout-changed', windowId, viewState)
}
}
diff --git a/src/renderer/store/project.js b/src/renderer/store/project.js
index bdd8845d..6cc70f85 100644
--- a/src/renderer/store/project.js
+++ b/src/renderer/store/project.js
@@ -82,7 +82,7 @@ const actions = {
showSideBar: true,
showTabBar: true
})
- dispatch('SET_LAYOUT_MENU_ITEM')
+ dispatch('DISPATCH_LAYOUT_MENU_ITEMS')
})
},
LISTEN_FOR_UPDATE_PROJECT ({ commit, state, dispatch }) {