Skip to content

Commit

Permalink
Refactor menu actions and shortcut handling (#3032)
Browse files Browse the repository at this point in the history
* Refactor menu actions and shortcut handling

* Remove duplicated command id

* Update docs
  • Loading branch information
fxha committed Mar 27, 2022
1 parent f01e30c commit cb57af4
Show file tree
Hide file tree
Showing 39 changed files with 932 additions and 482 deletions.
2 changes: 1 addition & 1 deletion docs/KEYBINDINGS_LINUX.md
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion docs/KEYBINDINGS_OSX.md
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion docs/KEYBINDINGS_WINDOWS.md
Expand Up @@ -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 |
Expand Down
110 changes: 110 additions & 0 deletions 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
19 changes: 18 additions & 1 deletion src/main/app/accessor.js
Expand Up @@ -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 {
/**
Expand All @@ -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
10 changes: 10 additions & 0 deletions src/main/app/env.js
Expand Up @@ -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
}
Expand Down Expand Up @@ -41,6 +42,13 @@ export class AppEnvironment {
return this._debug
}

/**
* @returns {boolean}
*/
get isDevMode () {
return this._isDevMode
}

/**
* @returns {boolean}
*/
Expand All @@ -65,13 +73,15 @@ 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']
const userDataPath = args['--user-data-dir'] // or null (= default user data path)

const appEnvironment = new AppEnvironment({
debug,
isDevMode,
verbose,
safeMode,
userDataPath
Expand Down
11 changes: 11 additions & 0 deletions 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)
}
53 changes: 53 additions & 0 deletions 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 }
36 changes: 36 additions & 0 deletions 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))
}
2 changes: 1 addition & 1 deletion src/main/keyboard/keybindingsDarwin.js
Expand Up @@ -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'],
Expand Down
2 changes: 1 addition & 1 deletion src/main/keyboard/keybindingsLinux.js
Expand Up @@ -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'],
Expand Down
2 changes: 1 addition & 1 deletion src/main/keyboard/keybindingsWindows.js
Expand Up @@ -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'],
Expand Down

0 comments on commit cb57af4

Please sign in to comment.