mirror of
https://github.com/marktext/marktext.git
synced 2025-05-03 04:51:28 +08:00
Fix application menu exceptions (#1437)
This commit is contained in:
parent
cc63cf87da
commit
2c15d516c3
@ -8,7 +8,6 @@ import { isChildOfDirectory } from 'common/filesystem/paths'
|
||||
import { isLinux, isOsx } from '../config'
|
||||
import parseArgs from '../cli/parser'
|
||||
import { normalizeMarkdownPath } from '../filesystem/markdown'
|
||||
import { getMenuItemById } from '../menu'
|
||||
import { selectTheme } from '../menu/actions/theme'
|
||||
import { dockMenu } from '../menu/templates'
|
||||
import { watchers } from '../utils/imagePathAutoComplement'
|
||||
@ -155,21 +154,15 @@ class App {
|
||||
() => {
|
||||
const preferences = this._accessor.preferences
|
||||
const { theme } = preferences.getAll()
|
||||
let setedTheme = null
|
||||
if (systemPreferences.isDarkMode() && theme !== 'dark') {
|
||||
|
||||
// Application menu is automatically updated via preference manager.
|
||||
if (systemPreferences.isDarkMode() && theme !== 'dark' &&
|
||||
theme !== 'material-dark' && theme !== 'one-dark') {
|
||||
selectTheme('dark')
|
||||
setedTheme = 'dark'
|
||||
}
|
||||
if (!systemPreferences.isDarkMode() && theme === 'dark') {
|
||||
if (!systemPreferences.isDarkMode() && theme !== 'light' &&
|
||||
theme !== 'ulysses' && theme !== 'graphite') {
|
||||
selectTheme('light')
|
||||
setedTheme = 'light'
|
||||
}
|
||||
if (setedTheme) {
|
||||
const themeMenu = getMenuItemById('themeMenu')
|
||||
const menuItem = themeMenu.submenu.items.filter(item => (item.id === setedTheme))[0]
|
||||
if (menuItem) {
|
||||
menuItem.checked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -429,7 +429,7 @@ class WindowManager extends EventEmitter {
|
||||
ipcMain.on('window-toggle-always-on-top', win => {
|
||||
const flag = !win.isAlwaysOnTop()
|
||||
win.setAlwaysOnTop(flag)
|
||||
this._appMenu.updateAlwaysOnTopMenu(flag)
|
||||
this._appMenu.updateAlwaysOnTopMenu(win.id, flag)
|
||||
})
|
||||
|
||||
ipcMain.on('broadcast-preferences-changed', prefs => {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import path from 'path'
|
||||
import { ipcMain, BrowserWindow } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import { updateLineEndingMenu } from '../../menu'
|
||||
import { searchFilesAndDir } from '../../utils/imagePathAutoComplement'
|
||||
|
||||
ipcMain.on('mt::ask-for-image-auto-path', (e, { pathname, src, id }) => {
|
||||
@ -22,10 +21,6 @@ ipcMain.on('mt::ask-for-image-auto-path', (e, { pathname, src, id }) => {
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('AGANI::update-line-ending-menu', (e, lineEnding) => {
|
||||
updateLineEndingMenu(lineEnding)
|
||||
})
|
||||
|
||||
export const edit = (win, type) => {
|
||||
win.webContents.send('AGANI::edit', { type })
|
||||
}
|
||||
|
@ -1,6 +1,3 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import { getMenuItemById } from '../../menu'
|
||||
|
||||
const MENU_ID_FORMAT_MAP = {
|
||||
strongMenuItem: 'strong',
|
||||
emphasisMenuItem: 'em',
|
||||
@ -11,17 +8,6 @@ const MENU_ID_FORMAT_MAP = {
|
||||
mathMenuItem: 'inline_math'
|
||||
}
|
||||
|
||||
const selectFormat = formats => {
|
||||
const formatMenuItem = getMenuItemById('formatMenuItem')
|
||||
formatMenuItem.submenu.items.forEach(item => (item.checked = false))
|
||||
formatMenuItem.submenu.items
|
||||
.forEach(item => {
|
||||
if (item.id && formats.some(format => format.type === MENU_ID_FORMAT_MAP[item.id])) {
|
||||
item.checked = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const format = (win, type) => {
|
||||
// Fix #961
|
||||
// TODO: This is not the best solution for fix #961, but we don't know how to reproduce this issue.
|
||||
@ -31,6 +17,18 @@ export const format = (win, type) => {
|
||||
win.webContents.send('AGANI::format', { type })
|
||||
}
|
||||
|
||||
ipcMain.on('AGANI::selection-formats', (e, formats) => {
|
||||
selectFormat(formats)
|
||||
})
|
||||
// --- IPC events -------------------------------------------------------------
|
||||
|
||||
// NOTE: Don't use static `getMenuItemById` here, instead request the menu by
|
||||
// window id from `AppMenu` manager.
|
||||
|
||||
export const updateFormatMenu = (applicationMenu, formats) => {
|
||||
const formatMenuItem = applicationMenu.getMenuItemById('formatMenuItem')
|
||||
formatMenuItem.submenu.items.forEach(item => (item.checked = false))
|
||||
formatMenuItem.submenu.items
|
||||
.forEach(item => {
|
||||
if (item.id && formats.some(format => format.type === MENU_ID_FORMAT_MAP[item.id])) {
|
||||
item.checked = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,3 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import { getMenuItemById } from '../../menu'
|
||||
|
||||
const DISABLE_LABELS = [
|
||||
// paragraph menu items
|
||||
'heading1MenuItem', 'heading2MenuItem', 'heading3MenuItem', 'heading4MenuItem',
|
||||
@ -29,21 +26,30 @@ const MENU_ID_MAP = {
|
||||
frontMatterMenuItem: 'pre'
|
||||
}
|
||||
|
||||
const setParagraphMenuItemStatus = bool => {
|
||||
const paragraphMenuItem = getMenuItemById('paragraphMenuEntry')
|
||||
export const paragraph = (win, type) => {
|
||||
win.webContents.send('AGANI::paragraph', { type })
|
||||
}
|
||||
|
||||
// --- IPC events -------------------------------------------------------------
|
||||
|
||||
// NOTE: Don't use static `getMenuItemById` here, instead request the menu by
|
||||
// window id from `AppMenu` manager.
|
||||
|
||||
const setParagraphMenuItemStatus = (applicationMenu, bool) => {
|
||||
const paragraphMenuItem = applicationMenu.getMenuItemById('paragraphMenuEntry')
|
||||
paragraphMenuItem.submenu.items
|
||||
.forEach(item => (item.enabled = bool))
|
||||
}
|
||||
|
||||
const setMultipleStatus = (list, status) => {
|
||||
const paragraphMenuItem = getMenuItemById('paragraphMenuEntry')
|
||||
const setMultipleStatus = (applicationMenu, list, status) => {
|
||||
const paragraphMenuItem = applicationMenu.getMenuItemById('paragraphMenuEntry')
|
||||
paragraphMenuItem.submenu.items
|
||||
.filter(item => item.id && list.includes(item.id))
|
||||
.forEach(item => (item.enabled = status))
|
||||
}
|
||||
|
||||
const setCheckedMenuItem = affiliation => {
|
||||
const paragraphMenuItem = getMenuItemById('paragraphMenuEntry')
|
||||
const setCheckedMenuItem = (applicationMenu, affiliation) => {
|
||||
const paragraphMenuItem = applicationMenu.getMenuItemById('paragraphMenuEntry')
|
||||
paragraphMenuItem.submenu.items.forEach(item => (item.checked = false))
|
||||
paragraphMenuItem.submenu.items.forEach(item => {
|
||||
if (!item.id) {
|
||||
@ -86,36 +92,32 @@ const setCheckedMenuItem = affiliation => {
|
||||
})
|
||||
}
|
||||
|
||||
export const paragraph = (win, type) => {
|
||||
win.webContents.send('AGANI::paragraph', { type })
|
||||
}
|
||||
|
||||
ipcMain.on('AGANI::selection-change', (e, { start, end, affiliation }) => {
|
||||
export const updateSelectionMenus = (applicationMenu, { start, end, affiliation }) => {
|
||||
// format menu
|
||||
const formatMenuItem = getMenuItemById('formatMenuItem')
|
||||
const formatMenuItem = applicationMenu.getMenuItemById('formatMenuItem')
|
||||
formatMenuItem.submenu.items.forEach(item => (item.enabled = true))
|
||||
// handle menu checked
|
||||
setCheckedMenuItem(affiliation)
|
||||
setCheckedMenuItem(applicationMenu, affiliation)
|
||||
// handle disable
|
||||
setParagraphMenuItemStatus(true)
|
||||
setParagraphMenuItemStatus(applicationMenu, true)
|
||||
|
||||
if (
|
||||
(/th|td/.test(start.type) && /th|td/.test(end.type)) ||
|
||||
(start.type === 'span' && start.block.functionType === 'codeLine') ||
|
||||
(end.type === 'span' && end.block.functionType === 'codeLine')
|
||||
) {
|
||||
setParagraphMenuItemStatus(false)
|
||||
setParagraphMenuItemStatus(applicationMenu, false)
|
||||
|
||||
if (start.block.functionType === 'codeLine' || end.block.functionType === 'codeLine') {
|
||||
setMultipleStatus(['codeFencesMenuItem'], true)
|
||||
setMultipleStatus(applicationMenu, ['codeFencesMenuItem'], true)
|
||||
formatMenuItem.submenu.items.forEach(item => (item.enabled = false))
|
||||
}
|
||||
} else if (start.key !== end.key) {
|
||||
formatMenuItem.submenu.items
|
||||
.filter(item => item.id && DISABLE_LABELS.includes(item.id))
|
||||
.forEach(item => (item.enabled = false))
|
||||
setMultipleStatus(DISABLE_LABELS, false)
|
||||
setMultipleStatus(applicationMenu, DISABLE_LABELS, false)
|
||||
} else if (!affiliation.slice(0, 3).some(p => /ul|ol/.test(p.type))) {
|
||||
setMultipleStatus(['looseListItemMenuItem'], false)
|
||||
setMultipleStatus(applicationMenu, ['looseListItemMenuItem'], false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { ipcMain, BrowserWindow } from 'electron'
|
||||
import { getMenuItemById } from '../../menu'
|
||||
|
||||
const sourceCodeModeMenuItemId = 'sourceCodeModeMenuItem'
|
||||
const typewriterModeMenuItemId = 'typewriterModeMenuItem'
|
||||
const focusModeMenuItemId = 'focusModeMenuItem'
|
||||
|
||||
@ -28,23 +26,14 @@ export const showTabBar = win => {
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.on('AGANI::ask-for-mode', e => {
|
||||
const sourceCodeModeMenuItem = getMenuItemById(sourceCodeModeMenuItemId)
|
||||
const typewriterModeMenuItem = getMenuItemById(typewriterModeMenuItemId)
|
||||
const focusModeMenuItem = getMenuItemById(focusModeMenuItemId)
|
||||
const modes = {
|
||||
sourceCode: sourceCodeModeMenuItem.checked,
|
||||
typewriter: typewriterModeMenuItem.checked,
|
||||
focus: focusModeMenuItem.checked
|
||||
}
|
||||
const win = BrowserWindow.fromWebContents(e.sender)
|
||||
win.webContents.send('AGANI::res-for-mode', modes)
|
||||
})
|
||||
// --- IPC events -------------------------------------------------------------
|
||||
|
||||
ipcMain.on('AGANI::set-view-layout', (e, { showSideBar, showTabBar }) => {
|
||||
const sideBarMenuItem = getMenuItemById('sideBarMenuItem')
|
||||
const tabBarMenuItem = getMenuItemById('tabBarMenuItem')
|
||||
// NOTE: Don't use static `getMenuItemById` here, instead request the menu by
|
||||
// window id from `AppMenu` manager.
|
||||
|
||||
export const viewLayoutChanged = (applicationMenu, { showSideBar, showTabBar }) => {
|
||||
const sideBarMenuItem = applicationMenu.getMenuItemById('sideBarMenuItem')
|
||||
const tabBarMenuItem = applicationMenu.getMenuItemById('tabBarMenuItem')
|
||||
sideBarMenuItem.checked = showSideBar
|
||||
tabBarMenuItem.checked = showTabBar
|
||||
})
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ import path from 'path'
|
||||
import { app, ipcMain, Menu } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import { ensureDirSync, isDirectory, isFile } from 'common/filesystem'
|
||||
import { isLinux } from '../config'
|
||||
import { isLinux, isOsx, isWindows } from '../config'
|
||||
import { parseMenu } from '../keyboard/shortcutHandler'
|
||||
import { updateFormatMenu } from '../menu/actions/format'
|
||||
import { updateSelectionMenus } from '../menu/actions/paragraph'
|
||||
import { viewLayoutChanged } from '../menu/actions/view'
|
||||
import configureMenu, { configSettingMenu } from '../menu/templates'
|
||||
|
||||
export const MenuType = {
|
||||
@ -28,14 +31,19 @@ class AppMenu {
|
||||
this._userDataPath = userDataPath
|
||||
|
||||
this.RECENTS_PATH = path.join(userDataPath, FILE_NAME)
|
||||
this.isOsxOrWindows = /darwin|win32/.test(process.platform)
|
||||
this.isOsx = process.platform === 'darwin'
|
||||
this.isOsxOrWindows = isOsx || isWindows
|
||||
this.isOsx = isOsx
|
||||
this.activeWindowId = -1
|
||||
this.windowMenus = new Map()
|
||||
|
||||
this._listenForIpcMain()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the file or directory path to the recently used documents.
|
||||
*
|
||||
* @param {string} filePath The file or directory full path.
|
||||
*/
|
||||
addRecentlyUsedDocument (filePath) {
|
||||
const { isOsxOrWindows, isOsx, MAX_RECENTLY_USED_DOCUMENTS, RECENTS_PATH } = this
|
||||
|
||||
@ -66,6 +74,11 @@ class AppMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all recently used documents and folders.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
getRecentlyUsedDocuments () {
|
||||
const { RECENTS_PATH, MAX_RECENTLY_USED_DOCUMENTS } = this
|
||||
if (!isFile(RECENTS_PATH)) {
|
||||
@ -86,6 +99,9 @@ class AppMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear recently used documents.
|
||||
*/
|
||||
clearRecentlyUsedDocuments () {
|
||||
const { isOsxOrWindows, isOsx, RECENTS_PATH } = this
|
||||
if (isOsxOrWindows) app.clearRecentDocuments()
|
||||
@ -98,34 +114,46 @@ class AppMenu {
|
||||
fs.writeFileSync(RECENTS_PATH, json, 'utf-8')
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a default menu to the given window.
|
||||
*
|
||||
* @param {number} windowId The window id.
|
||||
*/
|
||||
addDefaultMenu (windowId) {
|
||||
const { windowMenus } = this
|
||||
const menu = this.buildSettingMenu() // Setting menu is also the fallback menu.
|
||||
const menu = this._buildSettingMenu() // Setting menu is also the fallback menu.
|
||||
windowMenus.set(windowId, menu)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the settings menu to the given window.
|
||||
*
|
||||
* @param {BrowserWindow} window The settings browser window.
|
||||
*/
|
||||
addSettingMenu (window) {
|
||||
const { windowMenus } = this
|
||||
const menu = this.buildSettingMenu()
|
||||
const menu = this._buildSettingMenu()
|
||||
windowMenus.set(window.id, menu)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the editor menu to the given window.
|
||||
*
|
||||
* @param {BrowserWindow} window The editor browser window.
|
||||
* @param {[*]} options The menu options.
|
||||
*/
|
||||
addEditorMenu (window, options = {}) {
|
||||
const isSourceMode = !!options.sourceCodeModeEnabled
|
||||
const { windowMenus } = this
|
||||
windowMenus.set(window.id, this.buildEditorMenu(true))
|
||||
windowMenus.set(window.id, this._buildEditorMenu(true))
|
||||
|
||||
const { menu, shortcutMap } = windowMenus.get(window.id)
|
||||
const currentMenu = Menu.getApplicationMenu() // the menu may be null
|
||||
updateMenuItemSafe(currentMenu, menu, 'sourceCodeModeMenuItem', !!options.sourceCodeModeEnabled)
|
||||
updateMenuItemSafe(currentMenu, menu, 'typewriterModeMenuItem', false)
|
||||
|
||||
// FIXME: Focus mode is being ignored when you open a new window - inconsistency.
|
||||
// updateMenuItemSafe(currentMenu, menu, 'focusModeMenuItem', false)
|
||||
// Set source-code editor if prefered.
|
||||
const sourceCodeModeMenuItem = menu.getMenuItemById('sourceCodeModeMenuItem')
|
||||
sourceCodeModeMenuItem.checked = isSourceMode
|
||||
|
||||
const { checked: isSourceMode } = menu.getMenuItemById('sourceCodeModeMenuItem')
|
||||
if (isSourceMode) {
|
||||
// BUG: When opening a file `typewriterMode` and `focusMode` will be reset by editor.
|
||||
// If source code mode is set the editor must not change the values.
|
||||
const typewriterModeMenuItem = menu.getMenuItemById('typewriterModeMenuItem')
|
||||
const focusModeMenuItem = menu.getMenuItemById('focusModeMenuItem')
|
||||
typewriterModeMenuItem.enabled = false
|
||||
@ -134,6 +162,11 @@ class AppMenu {
|
||||
this._keybindings.registerKeyHandlers(window, shortcutMap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove menu from the given window.
|
||||
*
|
||||
* @param {number} windowId The window id.
|
||||
*/
|
||||
removeWindowMenu (windowId) {
|
||||
// NOTE: Shortcut handler is automatically unregistered when window is closed.
|
||||
const { activeWindowId } = this
|
||||
@ -143,19 +176,35 @@ class AppMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window menu.
|
||||
*
|
||||
* @param {number} windowId The window id.
|
||||
* @returns {Electron.Menu} The menu.
|
||||
*/
|
||||
getWindowMenuById (windowId) {
|
||||
const menu = this.windowMenus.get(windowId)
|
||||
if (!menu) {
|
||||
log.error(`getWindowMenuById: Cannot find window menu for id ${windowId}.`)
|
||||
log.error(`getWindowMenuById: Cannot find window menu for window id ${windowId}.`)
|
||||
throw new Error(`Cannot find window menu for id ${windowId}.`)
|
||||
}
|
||||
return menu.menu
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given window has a menu.
|
||||
*
|
||||
* @param {number} windowId The window id.
|
||||
*/
|
||||
has (windowId) {
|
||||
return this.windowMenus.has(windowId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given window as last active.
|
||||
*
|
||||
* @param {number} windowId The window id.
|
||||
*/
|
||||
setActiveWindow (windowId) {
|
||||
if (this.activeWindowId !== windowId) {
|
||||
// Change application menu to the current window menu.
|
||||
@ -164,36 +213,13 @@ class AppMenu {
|
||||
}
|
||||
}
|
||||
|
||||
buildEditorMenu (createShortcutMap, 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
|
||||
}
|
||||
}
|
||||
|
||||
buildSettingMenu () {
|
||||
if (this.isOsx) {
|
||||
const menuTemplate = configSettingMenu(this._keybindings)
|
||||
const menu = Menu.buildFromTemplate(menuTemplate)
|
||||
return { menu, type: MenuType.SETTINGS }
|
||||
}
|
||||
return { menu: null, type: MenuType.SETTINGS }
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all window menus.
|
||||
*
|
||||
* NOTE: We need this method to add or remove menu items at runtime.
|
||||
*
|
||||
* @param {[string[]]} recentUsedDocuments
|
||||
*/
|
||||
updateAppMenu (recentUsedDocuments) {
|
||||
if (!recentUsedDocuments) {
|
||||
recentUsedDocuments = this.getRecentlyUsedDocuments()
|
||||
@ -208,7 +234,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(false, recentUsedDocuments)
|
||||
|
||||
// all other menu items are set automatically
|
||||
updateMenuItem(oldMenu, newMenu, 'sourceCodeModeMenuItem')
|
||||
@ -228,25 +254,50 @@ class AppMenu {
|
||||
})
|
||||
}
|
||||
|
||||
updateLineEndingMenu (lineEnding) {
|
||||
updateLineEndingMenu(lineEnding)
|
||||
/**
|
||||
* Update line ending menu items.
|
||||
*
|
||||
* @param {number} windowId The window id.
|
||||
* @param {string} lineEnding Either >lf< or >crlf<.
|
||||
*/
|
||||
updateLineEndingMenu (windowId, lineEnding) {
|
||||
const menus = this.getWindowMenuById(windowId)
|
||||
const crlfMenu = menus.getMenuItemById('crlfLineEndingMenuEntry')
|
||||
const lfMenu = menus.getMenuItemById('lfLineEndingMenuEntry')
|
||||
if (lineEnding === 'crlf') {
|
||||
crlfMenu.checked = true
|
||||
} else {
|
||||
lfMenu.checked = true
|
||||
}
|
||||
}
|
||||
|
||||
updateAlwaysOnTopMenu (flag) {
|
||||
const menus = Menu.getApplicationMenu()
|
||||
/**
|
||||
* Update always on top menu item.
|
||||
*
|
||||
* @param {number} windowId The window id.
|
||||
* @param {boolean} lineEnding Always on top.
|
||||
*/
|
||||
updateAlwaysOnTopMenu (windowId, flag) {
|
||||
const menus = this.getWindowMenuById(windowId)
|
||||
const menu = menus.getMenuItemById('alwaysOnTopMenuItem')
|
||||
menu.checked = flag
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all theme entries from editor menus to the selected one.
|
||||
*/
|
||||
updateThemeMenu = theme => {
|
||||
this.windowMenus.forEach((value, key) => {
|
||||
this.windowMenus.forEach(value => {
|
||||
const { menu, type } = value
|
||||
if (type !== MenuType.EDITOR) return
|
||||
if (type !== MenuType.EDITOR) {
|
||||
return
|
||||
}
|
||||
|
||||
const themeMenus = menu.getMenuItemById('themeMenu')
|
||||
if (!themeMenus) {
|
||||
return
|
||||
}
|
||||
|
||||
themeMenus.submenu.items.forEach(item => (item.checked = false))
|
||||
themeMenus.submenu.items
|
||||
.forEach(item => {
|
||||
@ -257,10 +308,15 @@ class AppMenu {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all auto save entries from editor menus to the given state.
|
||||
*/
|
||||
updateAutoSaveMenu = autoSave => {
|
||||
this.windowMenus.forEach((value, key) => {
|
||||
this.windowMenus.forEach(value => {
|
||||
const { menu, type } = value
|
||||
if (type !== MenuType.EDITOR) return
|
||||
if (type !== MenuType.EDITOR) {
|
||||
return
|
||||
}
|
||||
|
||||
const autoSaveMenu = menu.getMenuItemById('autoSaveMenuItem')
|
||||
if (!autoSaveMenu) {
|
||||
@ -270,10 +326,15 @@ class AppMenu {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all aidou entries from editor menus to the given state.
|
||||
*/
|
||||
updateAidouMenu = bool => {
|
||||
this.windowMenus.forEach((value, key) => {
|
||||
this.windowMenus.forEach(value => {
|
||||
const { menu, type } = value
|
||||
if (type !== MenuType.EDITOR) return
|
||||
if (type !== MenuType.EDITOR) {
|
||||
return
|
||||
}
|
||||
|
||||
const aidouMenu = menu.getMenuItemById('aidou')
|
||||
if (!aidouMenu) {
|
||||
@ -283,6 +344,11 @@ class AppMenu {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Append misc shortcuts the the given shortcut map.
|
||||
*
|
||||
* @param {*} lineEnding The shortcut map.
|
||||
*/
|
||||
_appendMiscShortcuts = shortcutMap => {
|
||||
shortcutMap.push({
|
||||
accelerator: this._keybindings.getAccelerator('tabsCycleForward'),
|
||||
@ -314,6 +380,36 @@ class AppMenu {
|
||||
})
|
||||
}
|
||||
|
||||
_buildEditorMenu (createShortcutMap, 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
|
||||
}
|
||||
}
|
||||
|
||||
_buildSettingMenu () {
|
||||
if (this.isOsx) {
|
||||
const menuTemplate = configSettingMenu(this._keybindings)
|
||||
const menu = Menu.buildFromTemplate(menuTemplate)
|
||||
return { menu, type: MenuType.SETTINGS }
|
||||
}
|
||||
return { menu: null, type: MenuType.SETTINGS }
|
||||
}
|
||||
|
||||
_setApplicationMenu (menu) {
|
||||
if (isLinux && !menu) {
|
||||
// WORKAROUND for Electron#16521: We cannot hide the (application) menu on Linux.
|
||||
@ -328,6 +424,18 @@ class AppMenu {
|
||||
ipcMain.on('mt::add-recently-used-document', (e, pathname) => {
|
||||
this.addRecentlyUsedDocument(pathname)
|
||||
})
|
||||
ipcMain.on('mt::update-line-ending-menu', (e, windowId, lineEnding) => {
|
||||
this.updateLineEndingMenu(windowId, lineEnding)
|
||||
})
|
||||
ipcMain.on('mt::update-format-menu', (e, windowId, formats) => {
|
||||
updateFormatMenu(this.getWindowMenuById(windowId), formats)
|
||||
})
|
||||
ipcMain.on('mt::view-layout-changed', (e, windowId, viewSettings) => {
|
||||
viewLayoutChanged(this.getWindowMenuById(windowId), viewSettings)
|
||||
})
|
||||
ipcMain.on('mt::editor-selection-changed', (e, windowId, changes) => {
|
||||
updateSelectionMenus(this.getWindowMenuById(windowId), changes)
|
||||
})
|
||||
|
||||
ipcMain.on('menu-add-recently-used', pathname => {
|
||||
this.addRecentlyUsedDocument(pathname)
|
||||
@ -356,18 +464,6 @@ const updateMenuItem = (oldMenus, newMenus, id) => {
|
||||
newItem.checked = oldItem.checked
|
||||
}
|
||||
|
||||
const updateMenuItemSafe = (oldMenus, newMenus, id, defaultValue) => {
|
||||
let checked = defaultValue
|
||||
if (oldMenus) {
|
||||
const oldItem = oldMenus.getMenuItemById(id)
|
||||
if (oldItem) {
|
||||
checked = oldItem.checked
|
||||
}
|
||||
}
|
||||
const newItem = newMenus.getMenuItemById(id)
|
||||
newItem.checked = checked
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
// HACKY: We have one application menu per window and switch the menu when
|
||||
@ -384,15 +480,4 @@ export const getMenuItemById = menuId => {
|
||||
return menus.getMenuItemById(menuId)
|
||||
}
|
||||
|
||||
export const updateLineEndingMenu = lineEnding => {
|
||||
const menus = Menu.getApplicationMenu()
|
||||
const crlfMenu = menus.getMenuItemById('crlfLineEndingMenuEntry')
|
||||
const lfMenu = menus.getMenuItemById('lfLineEndingMenuEntry')
|
||||
if (lineEnding === 'crlf') {
|
||||
crlfMenu.checked = true
|
||||
} else {
|
||||
lfMenu.checked = true
|
||||
}
|
||||
}
|
||||
|
||||
export default AppMenu
|
||||
|
@ -125,6 +125,7 @@ export default function (keybindings, userPreference) {
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
// TODO: Remove this menu entry and add it to the command palette (#1408).
|
||||
label: 'Line Ending',
|
||||
submenu: [{
|
||||
id: 'crlfLineEndingMenuEntry',
|
||||
@ -141,8 +142,6 @@ export default function (keybindings, userPreference) {
|
||||
actions.lineEnding(browserWindow, 'lf')
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
type: 'separator'
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ class EditorWindow extends BaseWindow {
|
||||
this.bringToFront()
|
||||
|
||||
const lineEnding = preferences.getPreferedEOL()
|
||||
appMenu.updateLineEndingMenu(lineEnding)
|
||||
appMenu.updateLineEndingMenu(this.id, lineEnding)
|
||||
|
||||
win.webContents.send('mt::bootstrap-editor', {
|
||||
addBlankTab,
|
||||
|
7
src/renderer/bootstrap.js
vendored
7
src/renderer/bootstrap.js
vendored
@ -25,8 +25,13 @@ const parseUrlArgs = () => {
|
||||
const theme = params.get('theme')
|
||||
const titleBarStyle = params.get('tbs')
|
||||
const userDataPath = params.get('udp')
|
||||
const windowId = params.get('wid')
|
||||
const windowId = Number(params.get('wid'))
|
||||
const type = params.get('type')
|
||||
|
||||
if (Number.isNaN(windowId)) {
|
||||
throw new Error('Error while parsing URL arguments: windowId!')
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
debug,
|
||||
|
@ -129,7 +129,6 @@ export default {
|
||||
// module: editor
|
||||
dispatch('LISTEN_SCREEN_SHOT')
|
||||
dispatch('ASK_FOR_USER_PREFERENCE')
|
||||
dispatch('ASK_FOR_MODE')
|
||||
dispatch('LISTEN_FOR_CLOSE')
|
||||
dispatch('LISTEN_FOR_SAVE_AS')
|
||||
dispatch('LISTEN_FOR_MOVE_TO')
|
||||
|
@ -374,18 +374,12 @@ const actions = {
|
||||
commit('EXCHANGE_TABS_BY_ID', tabIDs)
|
||||
},
|
||||
|
||||
// need update line ending when change between windows.
|
||||
LISTEN_FOR_LINEENDING_MENU ({ commit, state, dispatch }) {
|
||||
ipcRenderer.on('AGANI::req-update-line-ending-menu', e => {
|
||||
dispatch('UPDATE_LINEENDING_MENU')
|
||||
})
|
||||
},
|
||||
|
||||
// need update line ending when change between tabs
|
||||
UPDATE_LINEENDING_MENU ({ commit, state }) {
|
||||
// We need to update line endings menu when changing tabs.
|
||||
UPDATE_LINE_ENDING_MENU ({ state }) {
|
||||
const { lineEnding } = state.currentFile
|
||||
if (lineEnding) {
|
||||
ipcRenderer.send('AGANI::update-line-ending-menu', lineEnding)
|
||||
const { windowId } = global.marktext.env
|
||||
ipcRenderer.send('mt::update-line-ending-menu', windowId, lineEnding)
|
||||
}
|
||||
},
|
||||
|
||||
@ -597,6 +591,7 @@ const actions = {
|
||||
if (!tabs.some(file => file.id === currentFile.id)) {
|
||||
commit('ADD_FILE_TO_TABS', currentFile)
|
||||
}
|
||||
dispatch('UPDATE_LINE_ENDING_MENU')
|
||||
},
|
||||
|
||||
// This events are only used during window creation.
|
||||
@ -682,7 +677,7 @@ const actions = {
|
||||
},
|
||||
|
||||
// Direction is a boolean where false is left and true right.
|
||||
CYCLE_TABS ({ commit, state }, direction) {
|
||||
CYCLE_TABS ({ commit, dispatch, state }, direction) {
|
||||
const { tabs, currentFile } = state
|
||||
if (tabs.length <= 1) {
|
||||
return
|
||||
@ -709,6 +704,7 @@ const actions = {
|
||||
return
|
||||
}
|
||||
commit('SET_CURRENT_FILE', nextTab)
|
||||
dispatch('UPDATE_LINE_ENDING_MENU')
|
||||
},
|
||||
|
||||
/**
|
||||
@ -915,11 +911,16 @@ const actions = {
|
||||
})
|
||||
}
|
||||
|
||||
ipcRenderer.send('AGANI::selection-change', changes)
|
||||
// TODO: We should only send a map of booleans to improve performance and not send
|
||||
// the full change with all block elements with every change.
|
||||
|
||||
const { windowId } = global.marktext.env
|
||||
ipcRenderer.send('mt::editor-selection-changed', windowId, changes)
|
||||
},
|
||||
|
||||
SELECTION_FORMATS ({ commit }, formats) {
|
||||
ipcRenderer.send('AGANI::selection-formats', formats)
|
||||
SELECTION_FORMATS (_, formats) {
|
||||
const { windowId } = global.marktext.env
|
||||
ipcRenderer.send('mt::update-format-menu', windowId, formats)
|
||||
},
|
||||
|
||||
// listen for export from main process
|
||||
|
@ -25,19 +25,20 @@ const mutations = {
|
||||
}
|
||||
|
||||
const actions = {
|
||||
LISTEN_FOR_LAYOUT ({ commit }, layout) {
|
||||
LISTEN_FOR_LAYOUT ({ commit }) {
|
||||
ipcRenderer.on('AGANI::listen-for-view-layout', (e, layout) => {
|
||||
commit('SET_LAYOUT', layout)
|
||||
})
|
||||
},
|
||||
LISTEN_FOR_REQUEST_LAYOUT ({ commit, dispatch }) {
|
||||
LISTEN_FOR_REQUEST_LAYOUT ({ dispatch }) {
|
||||
ipcRenderer.on('AGANI::request-for-view-layout', () => {
|
||||
dispatch('SET_LAYOUT_MENU_ITEM')
|
||||
})
|
||||
},
|
||||
SET_LAYOUT_MENU_ITEM ({ commit, state }) {
|
||||
SET_LAYOUT_MENU_ITEM ({ state }) {
|
||||
const { windowId } = global.marktext.env
|
||||
const { showTabBar, showSideBar } = state
|
||||
ipcRenderer.send('AGANI::set-view-layout', { showTabBar, showSideBar })
|
||||
ipcRenderer.send('mt::view-layout-changed', windowId, { showTabBar, showSideBar })
|
||||
},
|
||||
CHANGE_SIDE_BAR_WIDTH ({ commit }, width) {
|
||||
commit('SET_SIDE_BAR_WIDTH', width)
|
||||
|
@ -99,18 +99,6 @@ const actions = {
|
||||
})
|
||||
},
|
||||
|
||||
ASK_FOR_MODE ({ commit }) {
|
||||
ipcRenderer.send('AGANI::ask-for-mode')
|
||||
ipcRenderer.on('AGANI::res-for-mode', (e, modes) => {
|
||||
Object.keys(modes).forEach(type => {
|
||||
commit('SET_MODE', {
|
||||
type,
|
||||
checked: modes[type]
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
SET_SINGLE_PREFERENCE ({ commit }, { type, value }) {
|
||||
// save to electron-store
|
||||
ipcRenderer.send('mt::set-user-preference', { [type]: value })
|
||||
|
Loading…
Reference in New Issue
Block a user