I have a button in my electron app that says 'open file', and when you click on it the open file dialog box comes up and I am able to select a file.
Although, how am I able to open the 'open file dialog' box when I click a menu item from the apps toolbar?
This is my label in a submenu for the toolbar menu:
label: 'Open',
accelerator: 'CmdOrCtrl+O'
I want to do something like:
label: 'Open',
accelerator: 'CmdOrCtrl+O',
role: 'open'
But there is no such role as 'open'.
How can I implement an on click event that opens the open file dialog box?
Main.js open file section:
const ipc = require('electron').ipcMain
const dialog = require('electron').dialog
ipc.on('open-file-dialog', function (event) {
dialog.showOpenDialog({
properties: ['openFile', 'openDirectory']
}, function (files) {
if (files) event.sender.send('selected-file', files)
})
})
index.js:
const ipc = require('electron').ipcRenderer
const selectDirBtn = document.getElementById('open')
selectDirBtn.addEventListener('click', function (event) {
ipc.send('open-file-dialog')
})
ipc.on('selected-file', function (event, path) {
document.getElementById('selected-file').innerHTML = `► ${path}`
document.getElementById('selected-file2').innerHTML = `${path}`
})
Prime: Use IPC to communicate between the main and render process
Here is an example:
// main.js
const { app, BrowserWindow, Menu, dialog, ipcMain } = require('electron')
// wait until the app is ready before you can do anything
app.whenReady().then(function() {
// setting up the main window object
const mainWindow = new BrowserWindow({
backgroundColor: '#FFF',
webPreferences: {
// devTools: true,
nodeIntegration: true
},
show: false,
})
// setting up the menu with just two items
const menu = Menu.buildFromTemplate([
{
label: 'Menu',
submenu: [
{
label:'Open File',
accelerator: 'CmdOrCtrl+O',
// this is the main bit hijack the click event
click() {
// construct the select file dialog
dialog.showOpenDialog({
properties: ['openFile']
})
.then(function(fileObj) {
// the fileObj has two props
if (!fileObj.canceled) {
mainWindow.webContents.send('FILE_OPEN', fileObj.filePaths)
}
})
// should always handle the error yourself, later Electron release might crash if you don't
.catch(function(err) {
console.error(err)
})
}
},
{
label:'Exit',
click() {
app.quit()
}
}
]
}
])
Menu.setApplicationMenu(menu)
// now run it
mainWindow.loadURL(`file://${__dirname}/index.html`)
mainWindow.show()
})
Not going to show the index.html you should know what to do, just include the render.js into the HTML document.
Now the render.js
// render.js
const { ipcRenderer } = window.require('electron')
ipcRenderer.on('FILE_OPEN', (event, args) => {
// here the args will be the fileObj.filePaths array
// do whatever you need to do with it
console.log('got FILE_OPEN', event, args)
})
This was tested run on Electron 9.X
I had two buttons, a non-visible input file and visible styled button.
<input type="file" id="fileId" style="display:none;" />
<button class="btn-lg btn-primary center-block" type="button"
id="btnLoadFile">Load File</button>
In the js, I set the styled button click event to trigger the input file click event.
document.getElementById('btnLoadFile').addEventListener("click", function(){
document.getElementById('fileId').click();
});
Then I have an on change event listener for the input file element, that does some operation with the file.
document.getElementById('fileId').addEventListener('change', function(e){
//use the file here
var files = e.target.files;
var f = files[0]; {
var reader = new FileReader();
var name = f.name;
reader.onload = function (e) {
console.log(e.target.result);
};
reader.readAsBinaryString(f);
}
});
Hope this helps.
Related
I have a list. Click one item and then pop-up window which is named open. It's focused when it first opens, but it's out of focus the second time.
enter image description here
when I click a button, the following code execute. But, winObj.focus() not working in electron. It's only focus the main window. How do I fix it?
chatList.js (build in react)
const onClickBtn = useCallback(
(chat) => {
const winObj = window.open(
`/chatting/${chat.url}`,
chat.url,
"",
true
);
winObj.focus();
},
[]
);
electron.js (same as public/main.js)
function createWindow() {
const win = new BrowserWindow({
width: 400,
height: 700,
webPreferences: {
nodeIntegration: true,
nativeWindowOpen: true,
preload: path.join(__dirname, "/../build/preload.js"),
},
});
const pubUrl = "https://---";
win.loadURL(pubUrl);
//when i click the button above, the event come into here.
win.webContents.setWindowOpenHandler((event) => {
return {
action: "allow",
overrideBrowserWindowOptions: {
...win.webContents.browserWindowOptions,
parent: win,
},
};
});
}
This is an existing bug with Electron.
win.focus() will not actually focus the BrowserWindow. You'll want to send an ipc message down to the main process, grab your BrowserWindow and call win.focus() from there.
I implemented a touch bar button to my program which loads a file. My question is: How can I click to a specific button on the website after that page has loaded when I press the touch bar button? Here is my main.js file:
const { app, BrowserWindow, TouchBar } = require('electron')
const { TouchBarButton } = TouchBar
const path = require('path')
let window
function createWindow() {
const win = new BrowserWindow({
width: 1366,
height: 758,
minWidth: 1300,
minHeight: 700,
icon: __dirname + 'logoSquare.icns',
webPreferences: {
//preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('login.html')
win.maximize()
return win
}
app.whenReady().then(() => {
window = createWindow()
const button = new TouchBarButton({
label: `🛫 Add Flight`,
accessibilityLabel: 'Add Flight',
backgroundColor: '#a20021',
click: () => {
window.loadFile('./flights.html')
},
});
const touchBar = new TouchBar({
items: [
button,
],
})
window.setTouchBar(touchBar);
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
I have just started learning electron.js and couldn't find a solution yet.
I found an alternate way to solve this issue. I sent a query and got that information with JavaScript. This is the main.js file that is responsible for sending the query:
const button = new TouchBarButton({
label: `🛫 Add Flight`,
accessibilityLabel: 'Add Flight',
backgroundColor: '#3a2e39',
click: () => {
//window.loadFile('./flights.html?addflight=1')
window.loadURL('file://'+__dirname+'/flights.html?addflight=1')
},
})
And this is the code that is responsible for receiving data in the flights.html file with JavaScript:
function addFlightRedir() {
if (location.search == '?addflight=1') {
addNewFlight(); // This is the code that I wanted to execute
}
}
addFlightRedir();
And since it is reusable with standard JS, now I can implement it to the other areas of my program as well (independent from the Touch Bar buttons).
I'm building a Grapesjs plugin and have added a 'jscript' trait to a button component, which appears as a codemirror textarea. The idea is for users to be able to edit some javascript code associated with a button. I can't seem to intercept the codemirror area's change event, at least, not the proper codemirror specific version.
Happily, when I edit the codemirror area and change focus, a regular 'change' event triggers the Grapejs onEvent handler within my plugin's editor.TraitManager.addType('jcodemirror-editor', {} - good. I can then store the contents of the codemirror area into the trait.
onEvent({ elInput, component, event }) {
let code_to_run = elInput.querySelector(".CodeMirror").CodeMirror.getValue()
component.getTrait('jscript').set('value', code_to_run);
},
However if we paste or backspace or delete etc. in the codemirror area then the regular 'change' event is never issued!
So I'm trying to intercept the deeper codemirror specific 'change' event which is usually intercepted via cm.on("change", function (cm, changeObj) {} and which is triggered more reliably (unfortunately also on each keystroke). How do I wire this codemirror specific event to trigger the usual onEvent({ elInput, component, event }) {} code?
I have a workaround in place in my https://jsfiddle.net/tcab/1rh7mn5b/ but would like to know the proper way to do this.
My Plugin:
function customScriptPlugin(editor) {
const codemirrorEnabled = true // otherwise trait editor is just a plain textarea
const script = function (props) {
this.onclick = function () {
eval(props.jscript)
}
};
editor.DomComponents.addType("customScript", {
isComponent: el => el.tagName == 'BUTTON' && el.hasAttribute && el.hasAttribute("data-scriptable"),
model: {
defaults: {
traits: [
{
// type: 'text',
type: 'jcodemirror-editor', // defined below
name: 'jscript',
changeProp: true,
}
],
script,
jscript: `let res = 1 + 3; console.log('result is', res);`,
'script-props': ['jscript'],
},
},
});
editor.TraitManager.addType('jcodemirror-editor', {
createInput({ trait }) {
const el = document.createElement('div');
el.innerHTML = `
<form>
<textarea id="myjscript" name="myjscript" rows="14">
</textarea>
</form>
</div>
`
if (codemirrorEnabled) {
const textareaEl = el.querySelector('textarea');
var myCodeMirror = CodeMirror.fromTextArea(textareaEl, {
mode: "javascript",
lineWrapping: true,
});
// This is the 'more accurate' codemirror 'change' event
// which is triggered key by key. We need it cos if we paste
// or backspace or delete etc. in codemirror then the
// regular 'change' event is never issued! But how do we get
// this event to trigger the proper, usual 'onEvent' below?
// Currently cheating and doing the onEvent work here with
// this special handler.
myCodeMirror.on("change", function (cm, changeObj) { // HACK
const component = editor.getSelected()
const code_to_run = myCodeMirror.getValue()
component.getTrait('jscript').set('value', code_to_run);
console.log('onEvent hack - (myCodeMirror change event) updating jscript trait to be:', code_to_run)
})
}
return el;
},
// UI textarea & codemirror 'change' events trigger this function,
// so that we can update the component 'jscript' trait property.
onEvent({ elInput, component, event }) {
let code_to_run
if (codemirrorEnabled)
code_to_run = elInput.querySelector(".CodeMirror").CodeMirror.getValue()
else
code_to_run = elInput.querySelector('textarea').value
console.log('onEvent - updating jscript trait to be:', code_to_run)
component.getTrait('jscript').set('value', code_to_run);
}, // onEvent
// Updates the trait area UI based on what is in the component.
onUpdate({ elInput, component }) {
console.log('onUpdate - component trait jscript -> UI', component.get('jscript'))
if (codemirrorEnabled) {
const cm = elInput.querySelector(".CodeMirror").CodeMirror
cm.setValue(component.get('jscript'))
// codemirror content doesn't appear till you click on it - fix with this trick
setTimeout(function () {
cm.refresh();
}, 1);
}
else {
const textareaEl = elInput.querySelector('textarea');
textareaEl.value = component.get('jscript')
// actually is this even needed as things still update automatically without it?
// textareaEl.dispatchEvent(new CustomEvent('change'));
}
}, // onUpdate
}) // addType
editor.BlockManager.add(
'btnRegular',
{
category: 'Basic',
label: 'Regular Button',
attributes: { class: "fa fa-square-o" },
content: '<button type="button">Click Me</button>',
});
editor.BlockManager.add(
'btnScriptable',
{
category: 'Scriptable',
label: 'Scriptable Button',
attributes: { class: "fa fa-rocket" },
content: '<button type="button" data-scriptable="true">Run Script</button>',
});
}
const editor = grapesjs.init({
container: '#gjs',
fromElement: 1,
height: '100%',
storageManager: { type: 0 },
plugins: ['gjs-blocks-basic', 'customScriptPlugin']
});
According to official Grapesjs documentation on traits integrating external ui components you can trigger the onEvent event manually by calling this.onChange(ev).
So within createInput I continued to intercept the more reliable myCodeMirror.on("change", ... event and within that handler triggered the onEvent manually viz:
editor.TraitManager.addType('jcodemirror-editor', {
createInput({ trait }) {
const self = this // SOLUTION part 1
const el = document.createElement('div');
el.innerHTML = `
<form>
<textarea id="myjscript" name="myjscript" rows="14">
</textarea>
</form>
</div>
`
if (codemirrorEnabled) {
const textareaEl = el.querySelector('textarea');
var myCodeMirror = CodeMirror.fromTextArea(textareaEl, {
mode: "javascript",
lineWrapping: true,
});
myCodeMirror.on("change", function (cm, changeObj) {
self.onChange(changeObj) // SOLUTION part 2
})
}
return el;
},
I want to force the user only be allowed to quit the app from the Menu or Tray icon. I have those configured and working, but every time I hit Cmd+W or Cmd+Q it exits the app...
I tried catching it with:
app.on('quit', e => e.preventDefault())
app.on('window-all-closed', e => e.preventDefault())
...but it doesn't seem to have any effect.
You can use Menu module to override the shortcut as below:
const { Menu, MenuItem } = require('electron')
const menu = new Menu()
menu.append(new MenuItem({
label: 'Quit',
accelerator: 'CmdOrCtrl+Q',
click: () => { console.log('Cmd + Q is pressed') }
}));
It's up to you to hide or show this menu item because 'Quit' suppose to exit the application
I am trying to add thumbnail toolbar with specified buttons in a taskbar layout of an application from it's doc. But it's showing some problem.
The main.js:
// Modules to control application life and create native browser window
const {app, BrowserWindow, ipcMain, dialog} = require('electron')
const path = require('path')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}
})
if (isSecondInstance) {
app.quit();
}
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
show: false,
width: 500,
height: 200,
minWidth: 500,
minHeight: 200,
transparent: true,
frame: false,
})
mainWindow.setResizable(false)
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
// mainWindow.webContents.openDevTools()
mainWindow.once('ready-to-show', () => {
mainWindow.show()
mainWindow.focus()
})
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
ipcMain.on('open-information-dialog', (event) => {
if (mainWindow) {
const options = {
type: 'info',
title: 'I am abled',
buttons: ['Ok'],
message: 'This is the way.'
}
dialog.showMessageBox(mainWindow, options, function () {})
}
})
ipcMain.on('close', (event) => {
app.quit()
})
ipcMain.on('minimize', (event) => {
mainWindow.minimize()
})
ipcMain.on('progress', (event, per) => {
mainWindow.setProgressBar(per)
})
mainWindow.setThumbarButtons([
{
tooltip: 'button1',
icon: path.join(__dirname, 'button1.png'),
click () { console.log('button1 clicked') }
}, {
tooltip: 'button2',
icon: path.join(__dirname, 'button2.png'),
flags: ['enabled', 'dismissonclick'],
click () { console.log('button2 clicked.') }
}
])
The error:
All of my mainWindow reference working fine but when I am trying to set thumbar buttons with setThumbarButtons() it's getting the problem. I just try this one and I don't get any error but it's not showing any buttons on taskber window,
The Code:
app.on('ready', function(){
console.log(mainWindow)
mainWindow.setThumbarButtons([
{
tooltip: 'button1',
icon: path.join(__dirname, 'start.png'),
click () { console.log('button1 clicked') }
}
])
})
I can't make any sense with this problem.
The problem is that when you run mainWindow.setThumbarButtons your window hasn't been created. Therefor mainWindow is undefined. Which is the error you are running into, and what it says in the error screenshot you posted.
Currently you are creating and setting your window in the createWindow function.
If you move your setThumbarButtons code into the createWindow function it should work.
Something like the following:
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
show: false,
width: 500,
height: 200,
minWidth: 500,
minHeight: 200,
transparent: true,
frame: false,
})
mainWindow.setResizable(false)
// and load the index.html of the app.
mainWindow.loadFile('index.html')
mainWindow.setThumbarButtons([
{
tooltip: 'button1',
icon: path.join(__dirname, 'start.png'),
click () { console.log('button1 clicked') }
}
])
// Open the DevTools.
// mainWindow.webContents.openDevTools()
mainWindow.once('ready-to-show', () => {
mainWindow.show()
mainWindow.focus()
})
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
That way you are creating the mainWindow first and defining it before calling that function. Otherwise mainWindow will be undefined which is what you were experiencing.