close current window by submenu item in ElectronJs - electron

I have two windows opened as you see in this picture:
screenshot
I want to close smaller window by clicking on "close" submenu.
here's my code:
const addMenuTemplate = [
{
label:'file',
submenu:[
{
label:'close',
click(){
//i dont know what to put here
}
}
]
}
]
what should I do ?
I've already test electron.remote.getCurrentWindow().close()

If you want to close the current window, you don't have to write a custom click handler. The close role of MenuItem will handle it for you.
const template = [
// ...
{
label: 'my-sub-menu',
submenu: [
{ role: 'close' },
{ role: 'quit' },
]
}
// ...
]
See a live example Gist openable in Electron Fiddle.

If you have the window stored in a variable you can use win.close().
And it will close the window.

Related

electron: BrowserWindow.on close prompt not working

I have a normal electron app using React. And I wan to show a prompt before being closed.
The expected behavior is When I click on the "X" button, the window has to stay open and show the dialog before closing it so that I can choose if I really want to quit or not.
But instead, this is what happens
The electron window closes before showing the dialog. And the dialog closes on its own before i click on any button.
Here is the code included in the main.dev.ts
mainWindow.on('close',(e) => {
var choice = dialog.showMessageBox(mainWindow,
{
type: 'question',
buttons: ['Yes', 'No'],
title: 'Confirm',
message: 'Are you sure you want to quit?'
});
if(choice == 1){
e.preventDefault();
}});
I've already checked if there are some helfpul questions here on stackoverflow but found none.
Your help would be appreciated.
If you show a confirmation prompt you need to call e.preventDefault(); either way to keep the window open.
Then if they click "Yes", close the window separately.
However you get a problem where once the window closes after the user clicks "Yes" it'll trigger the close event again, causing an infinite loop. I've fixed this problem with the hasConfirmedClose variable below.
Here's what'll work:
var hasConfirmedClose = false;
mainWindow.on('close', (e) => {
if (!hasConfirmedClose) {
e.preventDefault(); // Prevent default no matter what.
var choice = dialog.showMessageBox(mainWindow, {
type: 'question',
buttons: ['Yes', 'No'],
title: 'Confirm',
message: 'Are you sure you want to quit?'
});
if (choice == 1) {
hasConfirmedClose = true;
mainWindow.close();
}
}
});

Calling app.quit() in electron app does nothing in Windows (but works on Linux)

I have an electron app that runs mostly in the background, occasionally opening a window in the bottom-right corner of the screen in response to messages received by a NATS client.
The app has a tray icon and menu with a quit button that calls electron.app.quit()
const createTray = () => {
const trayIconPath = path.join(__dirname, "icon.png");
tray = new Tray(trayIconPath);
const menuTemplate = [
{
label: `Version: ${app.getVersion()}`,
type: "normal",
enabled: false
},
...(other buttons here)...
{
label: "Quit",
type: "normal",
click: app.quit
},
{ label: "Exit", type: "normal", click: app.exit }
];
const contextMenu = Menu.buildFromTemplate(menuTemplate);
tray.setToolTip("Tooltip text");
tray.setContextMenu(contextMenu);
};
On linux, this button works fine. The app quits, and when I check ps, I find that the task is no longer running.
On windows, it doesn't seem to do anything. The app continues running, the icon remains in the tray, and if I click any of the other buttons in the tray, they act normally as if the app were never closed.
Note: the exit button works on both platforms, but doesn't fire the will-quit event, which I would like to add a handler to.
To attempt to debug, I've tried adding handlers to the before-quit, will-quit and quit events:
app.on("before-quit", () => {
console.log("before-quit");
dialog.showMessageBoxSync(null, {
title: "Before Quit",
message: "Before Quit",
buttons: ["OK"]
});
});
app.on("will-quit", () => {
console.log("will-quit");
dialog.showMessageBoxSync(null, {
title: "Will Quit",
message: "Will Quit",
buttons: ["OK"]
});
});
app.on("quit", () => {
console.log("did quit");
dialog.showMessageBoxSync(null, {
title: "Did Quit",
message: "Did Quit",
buttons: ["OK"]
});
});
On Linux, after pressing Quit, I see all three dialogs open before the app quits.
On Windows, after pressing Quit, I see the Before Quit dialog open, but neither of the other two do.
There are no other handlers on these events at the moment.
I have tried adding a try/catch block around the call to app.quit by replacing the click handler of the Quit button with the following method:
const quit = () => {
try {
mainWindow.close();
app.quit();
} catch (err) {
console.error(err);
dialog.showMessageBoxSync(null, {
title: "Error quitting",
message: err.message,
buttons: ["OK"]
});
}
};
After making this change, and trying to quit the app on Windows, I still see the Before Quit dialog, but do not see any error dialog, and the app continues to run.
So, what is preventing my app from quitting properly in Windows? Is there anything more I can do to debug the issue?
Updating from electron 6.0.1 to 6.0.10 seems to have resolved this issue

StackNavigation don't add screen to stack React Native

I have the current flow of screens.
Home -> Info -> Log in -> Camera.
The issue is that I don't want to add Login to the stack because then when you go back from the camera you will go back to login, rather than information.
If I use this.props.navigate('Info') from the camera, then the issue is that back now goes to the camera rather than home from info. Note I also don't want to complete delete the stack at the login page as I've seen with the Reset function here (https://reactnavigation.org/docs/navigators/navigation-actions#Reset).
Ideally what I would like to do would be:
Home -> Info -> Login (Not added to stack) -> Camera.
This was I can just go back from Camera to Info and from Info to Home.
Has anyone found a nice way around this?
Thanks
If you want to retain the whole stack and params, only ditching the Login page, then you could use Reset Navigation Action either in the Camera page :
import { NavigationActions } from 'react-navigation'
const resetAction = NavigationActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'Home', params: this.props.navigation.state.params })
NavigationActions.navigate({ routeName: 'Info', params: this.props.navigation.state.params })
]
})
this.props.navigation.dispatch(resetAction)
This way you will:
Navigate to Info page on top of Home page, meaning that Back button will lead to Home page.
Retain the params, if you need. If you don't just do not pass them.
This functionality could be added anywhere: inside the components / inside the Action Creators / inside the Back button.
Or add the same code for the Back button. In this case you need to add this to the navigationOptions of Camera route:
const backButton = ({ navigation }) => ({
headerLeft: <TouchableOpacity
onPress={() => {
// the code from above, except for import line and using navigation.whatever instead of this.props.navigation.whatever
}}
><Text>Back</Text> // this is a text of a back button, you could also use an icon
</TouchableOpacity>
});
Navigator:
const MyStack = StackNavigator({
home: { screen: Home },
info: { screen: Info },
login: { screen: Login },
camera: { screen: Camera }, navigationOptions: backButton });
With v6 of Navigation, CommonActions.reset might might work for you:
import { useNavigation,CommonActions } from "#react-navigation/native";
...
const navigation = useNavigation();
...
navigation.dispatch((state) => {
// We can keep all routes on the stack except that which has name "Login"
const routesWithoutLogin = state.routes.filter((r) => r.name !== "Login");
// We can then define the new route we want
const routeToAdd = {
name: "Info",
params: {},
};
const newRoutes = [...routesWithoutLogin, routeToAdd];
return CommonActions.reset({
...state,
routes: newRoutes,
index: newRoutes.length - 1,
});
});
Reference here: https://reactnavigation.org/docs/navigation-actions/#reset
When debugging, I find it quite useful to log my routes state to see what needs changing.
import { useNavigation,CommonActions } from "#react-navigation/native";
...
const navigation = useNavigation();
...
console.log(navigation.getState().routes);
It should look something like this
[{"key": "Root-YhPzpMumgvLs62ZwxjOJW", "name": "Root", "params": undefined}, {"key": "Chat-sDA1-4R0fImaxaj9maXGb", "name": "Chat", "params": {"conversation": [Object]}, "path": undefined}]
One more note, when I tried creating routes from scratch, I lost some of my redux state. It seems like a better idea to filter out routes you don't want than to make them from scratch.

Blank Firefox addon panel page with multiple windows

I've followed MDN's document to create a toggle button addon.
Everything works fine except one problem:
Open a second browser window (cmd+n or ctrl+n) and click on the toggle button there
Click on the toggle button on the original browser window without closing the toggle button on the second window
the toggle button's panel becomes blank with the following error message:
JavaScript error: resource:///modules/WindowsPreviewPerTab.jsm, line 406: NS_ERR
OR_FAILURE: Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIT
askbarTabPreview.invalidate]
// ./lib/main.js
var { ToggleButton } = require("sdk/ui/button/toggle");
var panels = require("sdk/panel");
var self = require("sdk/self");
var buttonIndex = 0;
var lastKnownButtonIndex = 0;
var button = ToggleButton({
id: "button",
label: "my button",
icon: {
"16": "./icon-16.png"
},
onClick: handleChange,
});
var panel = panels.Panel({
contentURL: self.data.url("menu.html"),
onHide: handleHide
});
function handleChange(state) {
if (state.checked) {
panel.show({
position: button
});
}
}
function handleHide() {
button.state('window', {checked: false});
}
function assignButtonIndex() {
return (buttonIndex++).toString();
}
The complete addon is here: https://goo.gl/9N3jle
To reproduce: Extract the zip file and $ cd testButton; cfx run and follow the above steps.
I really hope someone can help me with this. Thank you in advance!
It's a bug; you're not doing anything wrong. It's a racing condition between the windows' focus events, and the panel's event, that prevent somehow the panel's hidden event to be emitted properly.
You can try to mitigate with a workaround the issue until is properly fixed. I added some explanation in the bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1174425#c2 but in short, you can try to add a setTimeout to delay a bit when the panel is shown, in order to avoid the racing condition with the window's focus. Something like:
const { setTimeout } = require("sdk/timers");
/* ... your code here ... */
function handleChange(state) {
if (state.checked) {
setTimeout(() => panel.show({ position: button }), 100);
}
}
I am currently using a workaround where I dynamically create a new Panel every time the user presses the toolbar button.
It is faster than the 100ms workaround and also handles a scenario where the user outright closes one of the browser windows while the panel is open. (The 100ms workaround fails in this case and a blank panel is still displayed.)
It works like this:
let myPanel = null;
const toolbarButton = ToggleButton({
...,
onChange: function (state) {
if (state.checked) {
createPanel();
}
}
});
function createPanel(){
// Prevent memory leaks
if(myPanel){
myPanel.destroy();
}
// Create a new instance of the panel
myPanel = Panel({
...,
onHide: function(){
// Destroy the panel instead of just hiding it.
myPanel.destroy();
}
});
// Display the panel immediately
myPanel.show();
}

Titanium: Cannot use Camera together with OptionDialog

I've created a simple interaction in Titanium where the user opens an Option Dialog and then selects "Take photo", which opens up the camera.
The problem here is that the app crashes if you do the following:
Open Option Dialog and select "Take photo" which opens up the camera
Hit Cancel
Open the Option Dialog again and select "Take photo" again
Hit Cancel again - the app crashes.
I get the following messages in the console:
Dismissing a view controller when it is not the top presented view
controller. Will probably crash now.
attempt to dismiss modal view controller whose view does not
currently appear. self = modalViewController =
CoreAnimation: warning, deleted thread with uncommitted CATransaction;
set CA_DEBUG_TRANSACTIONS=1 in environment to log backtraces.
MPUSystemMediaControls] Updating supported commands for now playing
application.
It looks like this issue has been fixed in native iOS development in Objective C in this Stackoverflow Question, but how can I fixed this with Titanium?
Here is my code
$.imageView.addEventListener("click", function (e) {
$.imageDialog.cancel = 1;
$.imageDialog.show();
$.imageDialog.addEventListener("click", function (event) {
if (event.index === 0) {
$.imageDialog.hide();
Titanium.Media.showCamera({
success: function (e) {
if (e.mediaType === Ti.Media.MEDIA_TYPE_PHOTO) {
$.beerImage.image = e.media;
theImage = e.media;
$.cameraImage.opacity = 0.5;
}
},
cancel: function (e) { console.log('Action was cancelled'); },
error: function (e) { console.log('An error happened'); },
allowEditing: true,
mediaTypes: [Titanium.Media.MEDIA_TYPE_PHOTO],
videoQuality: Titanium.Media.QUALITY_HIGH
});
}
});
});
I have no problems at all if I do not use the Option Dialog and go straight to the Camera. Is is not possible to use both the Option Dialog and the Camera without having this issue? Or have a made a mistake in my code?
I found a work around for this.
The trick is to create a new OptionDialog each time the Camera needs to be opened. In the code below, the Option Dialog gets created when the button is clicked to open the camera. Therefore a new OptionDialog is created, the event listers are added to it and then it is opened.
$.imageView.addEventListener("click", function (e) {
var opts = {
cancel: 2,
options: ['Take Photo', 'Choose from gallery', 'Cancel'],
destructive: 0,
title: 'Choose'
};
var dialog = Ti.UI.createOptionDialog(opts);
dialog.addEventListener("click", function (event) {
if (event.index === 0) {
Titanium.Media.showCamera({
success: function () { ... },
cancel: function () { ... },
error: function () { ... },
allowEditing: true,
mediaTypes: [Titanium.Media.MEDIA_TYPE_PHOTO],
videoQuality: Titanium.Media.QUALITY_HIGH
});
}
$.addBeerWin.remove(dialog);
});
dialog.show();
});

Resources