Refactor menu actions and shortcut handling (#3032)

* Refactor menu actions and shortcut handling

* Remove duplicated command id

* Update docs
This commit is contained in:
Felix Häusler 2022-03-27 04:54:02 +02:00 committed by GitHub
parent f01e30c59a
commit cb57af4b2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 940 additions and 490 deletions

View File

@ -8,7 +8,7 @@ MarkText key bindings for Linux. Please see [general key bindings](KEYBINDINGS.m
| Id | Default | Description |
|:------------------- | --------------------------------------------- | ------------------------------------- |
| `file.new-file` | <kbd>Ctrl</kbd>+<kbd>N</kbd> | New file |
| `file.new-window` | <kbd>Ctrl</kbd>+<kbd>N</kbd> | New window |
| `file.new-tab` | <kbd>Ctrl</kbd>+<kbd>T</kbd> | New tab |
| `file.open-file` | <kbd>Ctrl</kbd>+<kbd>O</kbd> | Open markdown file |
| `file.open-folder` | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>O</kbd> | Open folder |

View File

@ -17,7 +17,7 @@ MarkText key bindings for macOS. Please see [general key bindings](KEYBINDINGS.m
| Id | Default | Description |
|:------------------- | ------------------------------------------------ | ------------------------------------- |
| `file.new-file` | <kbd>Command</kbd>+<kbd>N</kbd> | New file |
| `file.new-window` | <kbd>Command</kbd>+<kbd>N</kbd> | New window |
| `file.new-tab` | <kbd>Command</kbd>+<kbd>T</kbd> | New tab |
| `file.open-file` | <kbd>Command</kbd>+<kbd>O</kbd> | Open markdown file |
| `file.open-folder` | <kbd>Command</kbd>+<kbd>Shift</kbd>+<kbd>O</kbd> | Open folder |

View File

@ -8,7 +8,7 @@ MarkText key bindings for Windows. Please see [general key bindings](KEYBINDINGS
| Id | Default | Description |
|:------------------- | --------------------------------------------- | ------------------------------------- |
| `file.new-file` | <kbd>Ctrl</kbd>+<kbd>N</kbd> | New file |
| `file.new-window` | <kbd>Ctrl</kbd>+<kbd>N</kbd> | New window |
| `file.new-tab` | <kbd>Ctrl</kbd>+<kbd>T</kbd> | New tab |
| `file.open-file` | <kbd>Ctrl</kbd>+<kbd>O</kbd> | Open markdown file |
| `file.open-folder` | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>O</kbd> | Open folder |

View File

@ -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

View File

@ -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

View File

@ -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

11
src/main/commands/file.js Normal file
View File

@ -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)
}

View File

@ -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 }

36
src/main/commands/tab.js Normal file
View File

@ -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))
}

View File

@ -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'],

View File

@ -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'],

View File

@ -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'],

View File

@ -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,22 +46,33 @@ 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.
// 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, () => {
callMenuCallback(item, win)
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)
})
}
}
}
openConfigInFileManager () {
@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -1,38 +1,93 @@
import { getMenuItemById } from '../../menu'
import { ipcMain } from 'electron'
import { COMMANDS } from '../../commands'
const typewriterModeMenuItemId = 'typewriterModeMenuItem'
const focusModeMenuItemId = 'focusModeMenuItem'
const toggleTypeMode = (win, type) => {
if (win && win.webContents) {
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 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) => {
if (win && win.webContents) {
win.webContents.send('mt::set-view-layout', { [type]: value || item.checked })
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 -------------------------------------------------------------
@ -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)

View File

@ -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)
}

View File

@ -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 () {

View File

@ -4,7 +4,11 @@ import * as actions from '../actions/file'
const dockMenu = Menu.buildFromTemplate([{
label: 'Open...',
click (menuItem, browserWindow) {
if (browserWindow) {
actions.openFile(browserWindow)
} else {
actions.newEditorWindow()
}
}
}, {
label: 'Clear Recent',

View File

@ -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)
}

View File

@ -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',

View File

@ -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)
}
}]
}

View File

@ -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'

View File

@ -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)
}
}]
}

View File

@ -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)
}
})
}

View File

@ -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)
}
}
}]

View File

@ -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"
},

View File

@ -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

View File

@ -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')
}
},

View File

@ -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')

View File

@ -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')
})
}

View File

@ -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)
}

View File

@ -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')

View File

@ -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)
}
}

View File

@ -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 }) {