key events in atom shell - javascript

Let's say I want to have a key event only on my compiled application with atom shell.
var app = require('app');
var BrowserWindow = require('browser-window');
require('crash-reporter').start();
app.on('ready', function() {
win = new BrowserWindow({ fullscreen: true, frame: false });
win.hide();
win.loadUrl("http://localhost:3000");
win.webContents.on('did-finish-load', function() {
win.show();
win.focus();
});
process.on('uncaughtException', app.quit);
});
How could I bind a keyboard event on the web browser? Eg,
win.on('keypress', 'left-arrow', function() {
win.webContents.goBack();
});

Also, apparently left arrow fires on key down, not key press. Credit: Detecting arrow key presses in JavaScript
I'm just learning about atom-shell, but couldn't you catch the keypress inside your UI (I think it's called the rendererer process), like you would in a typical web page, then use the remote() API to call back to the renderer process and do whatever logic you wanted?

Related

Pressing Win + D causes an electron window that is not allowed to minimize to minimize on Windows 10

Using electron 16.9.1, I have built an app whose window is not allowed to minimize and whose alwaysOnTop property is set to false. To put it another way, the idea is to have a window that cannot be minimized and which is always "underneath" other windows.
The configuration of the window looks like this:
const win = new BrowserWindow({
resizable: false,
movable: false,
minimizable: false,
maximizable: false,
alwaysOnTop: true,
fullscreenable: false,
show: false,
frame: false,
backgroundColor: "#00FFFFFF",
transparent: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
It works fine, the functions of the app function as I have planned, with but one tiny flaw: once I use the Win + D shortcut, the window is minimized. I am aware that many apps behave this way, but what I really want to know is whether there is a way to avoid this.
[Update]
I realized the impossibility of what I am trying to ask for, and instead, I am now trying another approach: to listen to a "show the desktop" event in electron. Every time that event is triggered, I will show the window again. And so, the problem has now changed to: how do I monitor such an event?
What I really want to achieve is allowing any window to get over it, but not the desktop.
Every time that event is triggered, I will show the window again. And so, the problem has now changed to: how do I monitor such an event?
When minimizable: true is set, the BrowserWindow does fire minimize event when Win+D and Win+M are pressed. So, we can set minimizable: true and listen to those, including user minimize, events and restore the window. As frame:false is set user won't see minimize button only option of clicking on taskbar icon will be available.
let mainWindow;
let counter = 0;
function createWindow() {
mainWindow = new BrowserWindow({
width: 600,
height: 300,
show: false,
resizable: false,
movable: false,
minimizable: true, /* keep it true */
maximizable: false,
alwaysOnTop: false,
fullscreenable: false,
frame: false,
backgroundColor: '#00FFFFFF',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
enableRemoteModule: false,
nodeIntegration: false,
},
});
mainWindow.loadURL(indexPath);
//try to restore window on minimize
mainWindow.on('minimize', (e) => {
e.preventDefault();
console.log('minimize', ++counter);
//try changing delay value
setTimeout(() => mainWindow.restore(), 200);
});
mainWindow.on('restore', () => {
console.log('restore', counter);
});
mainWindow.once('ready-to-show', () => {
mainWindow.show();
if (dev) {
mainWindow.webContents.openDevTools();
}
});
mainWindow.on('closed', function () {
mainWindow = null;
});
}
Desktop doesn't come infront immediately it takes time so we can't keep delay too low to restore our window. I've deliberately kept the delay high in the code for the demo. Once you call restore, next restore calls have no effect. Drawback is user will be able click on the application icon in taskbar and the application will bounce back.
Now We have to deal with disabling keyboard shortcut Win-D,
so we use globalShortcut module.
const { app, globalShortcut } = require('electron')
globalShortcut.unregister('Super+D')
try these keys INSTEAD of Super:
Meta
Also try Capital D and small d
PLEASE ALSO TRY:
globalShortcut.register('Super+D', function() {
console.log('Blocked');
return false;
})
Don't get confused that I am asking you to register the key that you actually asked to unregister, THIS IDEA OF REGISTERING KEY CAME AS someone in GitHub said
Registering key in electron app makes it not to work anywhere.
Refer documentation:https://www.electronjs.org/docs/latest/api/global-shortcut#globalshortcutregisteraccelerator-callback
P. S I am sure this won't mostly work as windows owns the right of Win key but try it, I ask you to try many times as I have not tried it 😁
See
https://stackoverflow.com/a/44308648/14862885
It introduces you to a new module electronLocalshortcut
try:
const {app, globalShortcut} = require('electron');
win = new BrowserWindow();
electronLocalshortcut.register(win, 'Super+D', () => {//Even this uses the same REGISTER method to disable shortcut
console.log('Windows Button pressed');
return false;
});

Confirm beforeunload in Electron

i know that there are hundreds of questions like: "How can i prevent close event in electron" or something like that.
After implementing a confirmation box (electron message box) in the beforeunload event i was able to close my app and cancel the close event. Since the dev tools are always open, i didn't recognize that it doesn't work while the dev tools are closed...
window.onbeforeunload = e =>
{
// show a message box with "save", "don't save", and "cancel" button
let warning = remote.dialog.showMessageBox(...)
switch(warning)
{
case 0:
console.log("save");
return;
case 1:
console.log("don't save");
return;
case 2:
console.log("cancel");
return false;
// e.returnValue = "false";
// e.returnValue = false;
}
};
So, when the dev tools are opened, i can close the app with saving, without saving and cancel the event.
When the dev tools are closed, the cancel button doesn't work anymore.
Btw.:
window.onbeforeunload = e =>
{
return false;
alert("foo");
};
will cancel the close event and obviously wouldn't show the message (doesn't matter if dev tools are open or closed)
window.onbeforeunload = e =>
{
alert("foo");
return false;
};
will cancel the close event after pressing ok if dev tools are open and will close the app after pressing ok if dev tools are closed
Intentionally i'm using the synchronous api of the message box and while i'm writing this question i figured out that a two windowed app (new remote.BrowserWindow()) will behave exactly like with the dev tools.
Has anyone an idea how i can resolve this problem?
Many thanks in advance
Instead of onbeforeunload prefer working with the event close. From this event, you'll be able to catch the closing event before the whole closure process is completed (event closed). With close, you'll be able to take the control and stop whenever you need the completion of the closure.
This is possible when you create your BrowserWindow, preferably in the main process:
// Create the browser window.
window = new BrowserWindow({});
// Event 'close'
window.on('close', (e) => {
// Do your control here
if (bToStop) {
e.preventDefault();
}
})
// Event 'closed'
window.on('closed', (e) => {
// Fired only if you didn't called e.preventDefault(); above!
})
In addition, be aware that the function e.preventDefault() is spreading in the whole code. If you need to be back to the natural behaviour of Electron, you need to toggle the variable e.defaultPrevented to false.
Actually, it seems e.preventDefault() function is handling the variable e.defaultPrevented to true until any change on it.
Maybe this will help someone with similar needs as i had, i have a react app wrapped in an electron app, the react app is agnostic to electron and can also run in the browser and the requirements i had was to show the default browser prompt, the infamous Leave Site? alert.
In the browser this is easy, for example with react i just do this:
useEffect(() => {
window.onbeforeunload = promptOnProjectLeave ? () => true : undefined;
return () => {
window.onbeforeunload = undefined;
}
}, [promptOnProjectLeave]);
Which will show the default browser Leave Site? prompt, but in electron this will only prevent the window from being closed without any action prompt asking you if you are sure, so my approach was a mix of this post and another post.
This is the solution
mainWindow.webContents.on('will-prevent-unload', (event) => {
const options = {
type: 'question',
buttons: ['Cancel', 'Leave'],
message: 'Leave Site?',
detail: 'Changes that you made may not be saved.',
};
const response = dialog.showMessageBoxSync(null, options)
if (response === 1) event.preventDefault();
});
This will allow me to use window.onbeforeunload in my react code as i would in the browser, in the browser i will get the default browser prompt and in electron i will get a message box :)
This is my first time working with electron so might be some ways to improve this but either way hope this helps someone, i know it would have helped me when i started with this task.
Update:
As I mentioned above in the comments of the accepted answer, the preventDefault was ignored on Windows. To be precise, I had it placed in the callback of a native electron dialog that opened when the user closed the app.
Therefore I have implemented a different approach:
let close: boolean = false
win.on('close', (ev: any) => {
if (close === false) {
ev.preventDefault()
dialog.showMessageBox({
type: 'warning',
buttons: ['Cancel', 'Ok'],
title: 'Do not forget to safe your changes',
cancelId: 0,
defaultId: 1,
noLink: true
}).then((val) => {
if (val.response === 0) {
// Cancel the close process
} else if (win) {
close = true
app.quit()
}
})
}
})
You can simply use 'pagehide' event. It seems to be working fine for electron apps. It works slightly different from 'beforeunload' as it can't prevent closing window/tab, but if you only need to do something before the page is closed(send some async request with navigator.sendBeacon(), etc) then this event might suit your needs.
You can read more info about it here, here and in the docs
Example of usage:
window.addEventListener('pagehide', () => {
window.navigator.sendBeacon(url, data);
}

Using the 'before-quit' event in electron (atom-shell)

I have a application which needs to make an API call before it quits (something like logout). As I still need access to some app data (redux store) for the API call so I decided to listen to the 'before-quit' event on app.
I tried the following code:
import {remote} from 'electron';
let loggedout = false;
remote.app.on('before-quit', (event) => {
if (loggedout) return; // if we are logged out just quit.
console.warn('users tries to quit');
// prevent the default which should cancel the quit
event.preventDefault();
// in the place of the setTimout will be an API call
setTimeout(() => {
// if api call was a success
if (true) {
loggedout = true;
remote.app.quit();
} else {
// tell the user log-out was not successfull. retry and quit after second try.
}
}, 1000);
});
The event never seems to fire or preventing shutdown does not work.
When I replace before-quit with browser-window-blur the event does fire and the code seems to work.
For reference I use Electron 1.2.8 (Due to some dependencies I cannot upgrade). I've double checked and before-quit event was already implemented in that version.
Any Ideas why this event does not seem to be fired?
Thanks in advance and happy holidays!
I had the same issue, this was my solution:
In renderer:
const { ipcRenderer } = require('electron')
window._saved = false
window.onbeforeunload = (e) => {
if (!window.saved) {
callSaveAPI(() => {
// success? quit the app
window._saved = true
ipcRenderer.send('app_quit')
window.onbeforeunload = null
})
}
e.returnValue = false
}
In main:
const { ipcMain } = require('electron')
// listen the 'app_quit' event
ipcMain.on('app_quit', (event, info) => {
app.quit()
})
There are 2 problems which prevented the code from working:
Somehow the 'before-quit' event does not fire in the rerender process. (not main.js).
Once I moved the eventlistener into the main-process preventing the default did not stop the windows from closing. This can only be done through adding an window.onbeforeunload function which returns false. Like suggested in this thread.
One caveat is that the return statement of onbeforeunload does not get updated. In my case I first returned false (to prevent the closing of the window). The second time it did not return false but it kept preventing the closing of the window.
I got around that through overriding the window.onbeforeunload with a new function which did not return false.

Disable backspace in Atom-shell

I've been scouring the interwebz and Atom-shell documentation trying to find out how to disable the back() functionality of the backspace key within a browser window.
I would prefer not to have to resort to a javascript onkeydown listener (which works) and rather use something more native and at more of the application level instead of the browser window level.
The only way I have figured out to do this without the onkeydown listener is with a global-shortcut and the ipc events in the Electron api.
First a disclaimer...
Disabling any key with a global shortcut really does disable it GLOBALLY on your computer! PLEASE BE CAREFUL WHEN USING GLOBAL SHORTCUTS!
If you forget to unregister your shortcut, or do not handle it properly, you will find it difficult to fix your mistake without backspace!
That said this is what worked for me...
const { app, ipcMain,
globalShortcut,
BrowserWindow,
} = require('electron');
app.on('ready', () => {
// Create the browser window
let mainWindow = new BrowserWindow({width: 800, height: 600});
// and load the index.html of the app
mainWindow.loadUrl('file://' + __dirname + '/index.html');
// Register a 'Backspace' shortcut listener when focused on window
mainWindow.on('focus', () => {
if (mainWindow.isFocused()) {
globalShortcut.register('Backspace', () => {
// Provide feedback or logging here
// If you leave this section blank, you will get no
// response when you try the shortcut (i.e. Backspace).
console.log('Backspace was pressed!'); //comment-out or delete when ready.
});
});
});
// ** THE IMPORTANT PART **
// Unregister a 'Backspace' shortcut listener when leaving window.
mainWindow.on('blur', () => {
globalShortcut.unregister('Backspace');
console.log('Backspace is unregistered!'); //comment-out or delete when ready.
});
});
Alternatively you could add the shortcut inside an ipc "Toggle" event handler like this...
// In the main process
ipcMain.on('disableKey-toggle', (event, keyToDisable) => {
if (!globalShortcut.isRegistered(keyToDisable){
globalShortcut.register(keyToDisable, () => {
console.log(keyToDisable+' is registered!'); //comment-out or delete when ready.
});
} else {
globalShortcut.unregister(keyToDisable);
console.log(keyToDisable+' is unregistered!'); //comment-out or delete when ready.
}
});
// In the render process send the accelerator of the keyToDisable.
// Here we use the 'Backspace' accelerator.
const { ipcRenderer } = require('electron');
ipcRenderer.send('disableKey-toggle', 'Backspace');

Why APP is exiting on back button Titanium

app.js
if (osname === 'android') {
Window = require('ui/handheld/android/SignIn');
}
else {
Window = require('ui/handheld/SignIn');
}
new Window().open();
SignIn.js
function SignIn() {
var self = Ti.UI.createWindow();
//Some design and sign-in validation code
...
var StatusMain = require('/ui/handheld/android/StatusMain');
new StatusMain(global_vars).open();
return self;
}
StatusMain.js
function StatusMain(global_vars) {
var self = Ti.UI.createWindow();
return self;
}
On StatusMain.js, screen When I click on device's back button APP exits instead of going back on SignIn.js screen
Any help will be highly appreciable!
Thanks in advance,
Mohsin
You can handle back button event like this in your code
window.addEventListener('android:back', function(){
// close your current window
});
I suggest you set the (Android specific) exitOnClose property on false when creating the new window:
http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.UI.Window-property-exitOnClose
exitOnClose : Boolean
CREATION-ONLY
Boolean value indicating if the application should exit when the Android Back button is > > pressed while the window is being shown.
You can only set this as a createWindow({...}) option. Setting it after window creation > has no effect.
StatusMain.js
function StatusMain(global_vars) {
var self = Ti.UI.createWindow({
exitOnClose: false
});
return self;
}
That should do the trick. Although the default value is false, it seems that your issue has something to do with that. I recommend experimenting with settings this property to true/false.
Word of advise, you should also test your app on a device if you haven't done so already. My experience with the Android emulators is rather inconsistent at some points.
StatusMain is a lightweight window. It doesn't create new Android activity, instead it shares with SignIn window activity. That's why your app closes when press back button. You need to create a heavyweight window to (specify fullscreen or navBarHidden properties) StatusMain window.
Set the modal property for the new window to true.
function StatusMain(global_vars) {
var self = Ti.UI.createWindow({
modal: true
});
return self;
}

Categories

Resources