From 62e29effbf375d3c5faf82e0fff08b0f03ff63a1 Mon Sep 17 00:00:00 2001 From: Ricky Miller Date: Fri, 3 Nov 2017 16:24:41 -0400 Subject: [PATCH] Add context menu (#2001) --- app/ui/contextmenu.js | 18 ++++++++++++++++++ app/ui/window.js | 6 ++++++ lib/actions/ui.js | 17 +++++++++++++++++ lib/components/term-group.js | 1 + lib/components/terms.js | 9 +++++++++ lib/constants/ui.js | 1 + lib/containers/terms.js | 6 ++++++ 7 files changed, 58 insertions(+) create mode 100644 app/ui/contextmenu.js diff --git a/app/ui/contextmenu.js b/app/ui/contextmenu.js new file mode 100644 index 00000000..0e291798 --- /dev/null +++ b/app/ui/contextmenu.js @@ -0,0 +1,18 @@ +const editMenu = require('../menus/menus/edit'); +const shellMenu = require('../menus/menus/shell'); +const {getKeymaps: commands} = require('../config'); +const separator = {type: 'separator'}; + +// only display cut/copy when there's a cursor selection +const filterCutCopy = (selection, menuItem) => { + if (/^cut$|^copy$/.test(menuItem.role) && !selection) { + return; + } + return menuItem; +}; + +module.exports = (createWindow, selection) => { + const _shell = shellMenu(commands, createWindow).submenu; + const _edit = editMenu(commands).submenu.filter(filterCutCopy.bind(null, selection)); + return _edit.concat(separator, _shell); +}; diff --git a/app/ui/window.js b/app/ui/window.js index 934ad331..6d2ba71c 100644 --- a/app/ui/window.js +++ b/app/ui/window.js @@ -11,6 +11,7 @@ const createRPC = require('../rpc'); const notify = require('../notify'); const fetchNotifications = require('../notifications'); const Session = require('../session'); +const contextMenuTemplate = require('./contextmenu'); const {execCommand} = require('../commands'); module.exports = class Window { @@ -153,6 +154,11 @@ module.exports = class Window { rpc.on('open external', ({url}) => { shell.openExternal(url); }); + rpc.on('open context menu', selection => { + const {createWindow} = app; + const {buildFromTemplate} = Menu; + buildFromTemplate(contextMenuTemplate(createWindow, selection)).popup(window); + }); rpc.on('open hamburger menu', ({x, y}) => { Menu.getApplicationMenu().popup(Math.ceil(x), Math.ceil(y)); }); diff --git a/lib/actions/ui.js b/lib/actions/ui.js index 2b30308f..118fc2e3 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -20,6 +20,7 @@ import { UI_WINDOW_GEOMETRY_CHANGED, UI_WINDOW_MOVE, UI_OPEN_FILE, + UI_CONTEXTMENU_OPEN, UI_COMMAND_EXEC } from '../constants/ui'; @@ -27,6 +28,22 @@ import {setActiveGroup} from './term-groups'; const {stat} = window.require('fs'); +export function openContextMenu(uid, selection) { + return (dispatch, getState) => { + dispatch({ + type: UI_CONTEXTMENU_OPEN, + uid, + effect() { + const state = getState(); + const show = !state.ui.quickEdit; + if (show) { + rpc.emit('open context menu', selection); + } + } + }); + }; +} + export function increaseFontSize() { return (dispatch, getState) => { dispatch({ diff --git a/lib/components/term-group.js b/lib/components/term-group.js index a57ec0e7..374f1c97 100644 --- a/lib/components/term-group.js +++ b/lib/components/term-group.js @@ -78,6 +78,7 @@ class TermGroup_ extends PureComponent { onTitle: this.bind(this.props.onTitle, null, uid), onData: this.bind(this.props.onData, null, uid), onURLAbort: this.bind(this.props.onURLAbort, null, uid), + onContextMenu: this.bind(this.props.onContextMenu, null, uid), borderColor: this.props.borderColor, quickEdit: this.props.quickEdit, uid diff --git a/lib/components/terms.js b/lib/components/terms.js index 048497ce..406b6585 100644 --- a/lib/components/terms.js +++ b/lib/components/terms.js @@ -64,6 +64,14 @@ export default class Terms extends Component { this.terms[uid] = term; } + componentDidMount() { + window.addEventListener('contextmenu', () => { + const selection = window.getSelection().toString(); + const {props: {uid}} = this.getActiveTerm(); + this.props.onContextMenu(uid, selection); + }); + } + componentWillUnmount() { this.props.ref_(null); } @@ -97,6 +105,7 @@ export default class Terms extends Component { onTitle: this.props.onTitle, onData: this.props.onData, onURLAbort: this.props.onURLAbort, + onContextMenu: this.props.onContextMenu, quickEdit: this.props.quickEdit, parentProps: this.props }); diff --git a/lib/constants/ui.js b/lib/constants/ui.js index 98c33c11..47a4a1cb 100644 --- a/lib/constants/ui.js +++ b/lib/constants/ui.js @@ -17,4 +17,5 @@ export const UI_OPEN_FILE = 'UI_OPEN_FILE'; export const UI_OPEN_HAMBURGER_MENU = 'UI_OPEN_HAMBURGER_MENU'; export const UI_WINDOW_MINIMIZE = 'UI_WINDOW_MINIMIZE'; export const UI_WINDOW_CLOSE = 'UI_WINDOW_CLOSE'; +export const UI_CONTEXTMENU_OPEN = 'UI_CONTEXTMENU_OPEN'; export const UI_COMMAND_EXEC = 'UI_COMMAND_EXEC'; diff --git a/lib/containers/terms.js b/lib/containers/terms.js index c2a10608..f2563385 100644 --- a/lib/containers/terms.js +++ b/lib/containers/terms.js @@ -7,6 +7,7 @@ import { setSessionXtermTitle, setActiveSession } from '../actions/sessions'; +import {openContextMenu} from '../actions/ui'; import getRootGroups from '../selectors'; const TermsContainer = connect( @@ -60,6 +61,11 @@ const TermsContainer = connect( onActive(uid) { dispatch(setActiveSession(uid)); + }, + + onContextMenu(uid, selection) { + dispatch(setActiveSession(uid)); + dispatch(openContextMenu(uid, selection)); } }; },