mirror of https://github.com/jiahaog/Nativefier
macOS: Add native tabs (PR #579)
Electron supports using native tabs on macOS (API added in Electron 1.8.1). This change adds a context menu item on platforms that support it (macOS for now) to open links in new tabs, and also adds support for {command,middle}-clicking links to open them in a new tab.
Maintainer (@ronjouch) note: this feature is macOS-only. Windows/Linux patches welcome 🙂.
This commit is contained in:
parent
025936e9c5
commit
ac99c6424d
|
@ -1,7 +1,7 @@
|
|||
import { shell } from 'electron';
|
||||
import contextMenu from 'electron-context-menu';
|
||||
|
||||
function initContextMenu(createNewWindow) {
|
||||
function initContextMenu(createNewWindow, createNewTab) {
|
||||
contextMenu({
|
||||
prepend: (params) => {
|
||||
const items = [];
|
||||
|
@ -18,6 +18,14 @@ function initContextMenu(createNewWindow) {
|
|||
createNewWindow(params.linkURL);
|
||||
},
|
||||
});
|
||||
if (createNewTab) {
|
||||
items.push({
|
||||
label: 'Open Link in New Tab',
|
||||
click: () => {
|
||||
createNewTab(params.linkURL, false);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ import createMenu from './../menu/menu';
|
|||
import initContextMenu from './../contextMenu/contextMenu';
|
||||
|
||||
const {
|
||||
isOSX, linkIsInternal, getCssToInject, shouldInjectCss, getAppIcon,
|
||||
isOSX, linkIsInternal, getCssToInject, shouldInjectCss, getAppIcon, nativeTabsSupported,
|
||||
} = helpers;
|
||||
|
||||
const ZOOM_INTERVAL = 0.1;
|
||||
|
@ -66,6 +66,7 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
|||
const DEFAULT_WINDOW_OPTIONS = {
|
||||
// Convert dashes to spaces because on linux the app name is joined with dashes
|
||||
title: options.name,
|
||||
tabbingIdentifier: nativeTabsSupported() ? options.name : undefined,
|
||||
webPreferences: {
|
||||
javascript: true,
|
||||
plugins: true,
|
||||
|
@ -105,18 +106,29 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
|||
fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
|
||||
}
|
||||
|
||||
const withFocusedWindow = (block) => {
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (focusedWindow) { block(focusedWindow); }
|
||||
};
|
||||
|
||||
const adjustWindowZoom = (window, adjustment) => {
|
||||
window.webContents.getZoomFactor((zoomFactor) => {
|
||||
window.webContents.setZoomFactor(zoomFactor + adjustment);
|
||||
});
|
||||
};
|
||||
|
||||
const onZoomIn = () => adjustWindowZoom(mainWindow, ZOOM_INTERVAL);
|
||||
const onZoomIn = () => {
|
||||
withFocusedWindow(focusedWindow => adjustWindowZoom(focusedWindow, ZOOM_INTERVAL));
|
||||
};
|
||||
|
||||
const onZoomOut = () => adjustWindowZoom(mainWindow, -ZOOM_INTERVAL);
|
||||
const onZoomOut = () => {
|
||||
withFocusedWindow(focusedWindow => adjustWindowZoom(focusedWindow, -ZOOM_INTERVAL));
|
||||
};
|
||||
|
||||
const onZoomReset = () => {
|
||||
mainWindow.webContents.setZoomFactor(options.zoom);
|
||||
withFocusedWindow((focusedWindow) => {
|
||||
focusedWindow.webContents.setZoomFactor(options.zoom);
|
||||
});
|
||||
};
|
||||
|
||||
const clearAppData = () => {
|
||||
|
@ -140,24 +152,48 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
|||
};
|
||||
|
||||
const onGoBack = () => {
|
||||
mainWindow.webContents.goBack();
|
||||
withFocusedWindow((focusedWindow) => {
|
||||
focusedWindow.webContents.goBack();
|
||||
});
|
||||
};
|
||||
|
||||
const onGoForward = () => {
|
||||
mainWindow.webContents.goForward();
|
||||
withFocusedWindow((focusedWindow) => {
|
||||
focusedWindow.webContents.goForward();
|
||||
});
|
||||
};
|
||||
|
||||
const getCurrentUrl = () => mainWindow.webContents.getURL();
|
||||
const getCurrentUrl = () => {
|
||||
withFocusedWindow((focusedWindow) => {
|
||||
focusedWindow.webContents.getURL();
|
||||
});
|
||||
};
|
||||
|
||||
let createNewWindow;
|
||||
|
||||
const onNewWindow = (event, urlToGo) => {
|
||||
if (mainWindow.useDefaultWindowBehaviour) {
|
||||
mainWindow.useDefaultWindowBehaviour = false;
|
||||
return;
|
||||
}
|
||||
const createNewTab = (url, foreground) => {
|
||||
withFocusedWindow((focusedWindow) => {
|
||||
const newTab = createNewWindow(url);
|
||||
focusedWindow.addTabbedWindow(newTab);
|
||||
if (!foreground) {
|
||||
focusedWindow.focus();
|
||||
}
|
||||
return newTab;
|
||||
});
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const onNewWindow = (event, urlToGo, _, disposition) => {
|
||||
event.preventDefault();
|
||||
if (nativeTabsSupported()) {
|
||||
if (disposition === 'background-tab') {
|
||||
createNewTab(urlToGo, false);
|
||||
return;
|
||||
} else if (disposition === 'foreground-tab') {
|
||||
createNewTab(urlToGo, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!linkIsInternal(options.targetUrl, urlToGo, options.internalUrls)) {
|
||||
shell.openExternal(urlToGo);
|
||||
return;
|
||||
|
@ -200,7 +236,7 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
|||
|
||||
createMenu(menuOptions);
|
||||
if (!options.disableContextMenu) {
|
||||
initContextMenu(createNewWindow);
|
||||
initContextMenu(createNewWindow, nativeTabsSupported() ? createNewTab : undefined);
|
||||
}
|
||||
|
||||
if (options.userAgent) {
|
||||
|
@ -233,10 +269,16 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
|||
}
|
||||
|
||||
mainWindow.webContents.on('new-window', onNewWindow);
|
||||
|
||||
mainWindow.loadURL(options.targetUrl);
|
||||
|
||||
mainWindow.on('new-tab', () => createNewTab(options.targetUrl, true));
|
||||
|
||||
mainWindow.on('close', (event) => {
|
||||
if (mainWindow.isFullScreen()) {
|
||||
if (nativeTabsSupported()) {
|
||||
mainWindow.moveTabToNewWindow();
|
||||
}
|
||||
mainWindow.setFullScreen(false);
|
||||
mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event, options.fastQuit));
|
||||
}
|
||||
|
@ -246,12 +288,4 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
|||
return mainWindow;
|
||||
}
|
||||
|
||||
ipcMain.on('cancelNewWindowOverride', () => {
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
allWindows.forEach((window) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
window.useDefaultWindowBehaviour = false;
|
||||
});
|
||||
});
|
||||
|
||||
export default createMainWindow;
|
||||
|
|
|
@ -59,6 +59,10 @@ function getAppIcon() {
|
|||
return path.join(__dirname, '../', `/icon.${isWindows() ? 'ico' : 'png'}`);
|
||||
}
|
||||
|
||||
function nativeTabsSupported() {
|
||||
return isOSX();
|
||||
}
|
||||
|
||||
export default {
|
||||
isOSX,
|
||||
isLinux,
|
||||
|
@ -68,4 +72,5 @@ export default {
|
|||
debugLog,
|
||||
shouldInjectCss,
|
||||
getAppIcon,
|
||||
nativeTabsSupported,
|
||||
};
|
||||
|
|
|
@ -118,6 +118,10 @@ app.on('ready', () => {
|
|||
createTrayIcon(appArgs, mainWindow);
|
||||
});
|
||||
|
||||
app.on('new-window-for-tab', () => {
|
||||
mainWindow.emit('new-tab');
|
||||
});
|
||||
|
||||
app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||
// for http authentication
|
||||
event.preventDefault();
|
||||
|
|
Loading…
Reference in New Issue