Accessing Submenu Items in Electron - menu

I would like to access the submenu elements in my menu.
My menu is organized like:
File
--stuff
Workflow
--item 1
--item 2
Help
--stuff
And the code that I have right now
var workflowItem = items.filter(function findWorkflowMenu(menuItem) {
return menuItem.label === 'Workflow'
})
if( workflowItem.length > 0 ) {
console.log('workflow item: ' )
console.log(workflowItem[0].menu.items) //this line doesn't seem right
}
The console output is showing all the menu again and not the sub-elements to Workflow.
What is the proper query to get the sub elements?

This is how it should be:
const {Menu} = require('electron');
let menu = new Menu([
'label': 'menu item',
'submenu': [{
'label': 'aLabel'
}]
}]);
console.log(menu[0].submenu);
If you can't get this to work on your app it means you have some problems in your filter or somewhere else.

Related

How to make a text field link be opened in a new tab, in Velo?

In Wix, I have a text field in a repeater that is used for navigating to other dynamic pages. The link works, but there are two problems with that. First, I have to click two times, not double click, for functioning the link. Second, I want to make the text field act as a button link, I mean be able to right click on that and choose 'open in new tab'. How can I fix these problems in my code?
Here is the code
// Navigating to related dynaic page
import wixLocation from 'wix-location';
export function ndText_click(event) {
$w("#repeater1").onItemReady(($item, itemData, index) => {
$item("#nText").onClick((event) => {
let postTypeValue = itemData.pType
wixData.query("Collection1").eq("_id", itemData._id)
.find()
.then(results => {
let item = results.items[0];
let pIDValue = item.postId;
if (postTypeValue == "R") {
wixLocation.to('/re/' + postIDValue);
} else if (postTypeValue == "L") {
wixLocation.to('/lo/' + postIDValue);
}
})
});
})
}
I suggest trying to use a button instead of the text element. You can usually style the button so it looks the same as the text element you already have. Then instead of setting the onClick, try setting the button's link and target properties.

How to remove view sub-menu in electron app for main window

Hi i want to remove 3 options from electron title menu they are
reload
Force reload
Toggle developer tools
as shown below
Here is what i have tried
background.js
import { app, session, protocol, BrowserWindow , dialog, ipcMain, Menu} from 'electron'
const menu = Menu.getApplicationMenu(); // get default menu
console.log('menu %s',menu.items);
let menuIndex = menu.items.findIndex(o => {
return o.role == 'viewmenu'
})
//splice items based on names , i'm not able to do that because i cannot see the full json object and its structure in console.log
//Menu.setApplicationMenu(menu); //set menu
Further i don't know what to do
Please help me thanks in advance !!
You can create a new menu based on the default using Menu.buildFromTemplate and pass the default's menu items as template.
Then set that as application menu. During the construction you can cut out elements you don't want
const menu = Menu.getApplicationMenu(); // get default menu
// build a new menu based on default one
const newmenu = Menu.buildFromTemplate(
menu.items.map(i => {
// overwrite viewmenu item
if (i.role === 'viewmenu') {
// create new submenu
const newviewsub = Menu.buildFromTemplate(
i.submenu.items.slice(4) // cut first 4 item (4th is separator)
)
// replace this item's submenu with the news
return Object.assign({}, i, { submenu: newviewsub })
}
// otherwise keep
return i
})
)
Menu.setApplicationMenu(newmenu)
You should see that the 'view' menu's first 3 element is gone

How to find ActiveDialog (waterfall-step) in context after replacedialog in waterfall dialog

Contextual help in prompts
I need to implement contextual help for a chatbot. My strategy is to use the active prompt as an index for a table with help-textlines. I am struggling with finding the active prompt after a stepContext.replaceDialog() in a waterfall dialog.
I will use the Compex Dialog sample as example.
In reviewSelectionDialog below is a prompt called CHOICE_PROMPT. This is the prompt in which I would like to add contextual help. If the user enters help, the helptext should be shown that is about that prompt.
In the same dialog is a loopstep. Based on a user decision, the dialog is repeated (looped) by the replaceDialog() method.
ReviewSelectionDialog is extended with CancelAndHelpDialog. As a result I am able to check for and act on any user interrupts like 'help'.
In CancelAndHelpDialog I need the active prompt when help was entered by the user so I am able to show relevant help. (CHOICE_PROMPT in this example).
My question
In the first pass of ReviewSelectionDialog, after sending 'help', I am able to get the active prompt in the CancelAndHelpDialog via innerDc.activeDialog.id. But after the stepContext.replaceDialog() in loopStep and sending 'help' again in the CHOICE_PROMPT, innerDc.activeDialog.id shows REVIEW_SELECTION_DIALOG. Where do I find the active prompt after a replace_dialog()?
ReviewSelectionDialog
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const { ChoicePrompt, WaterfallDialog } = require('botbuilder-dialogs');
const REVIEW_SELECTION_DIALOG = 'REVIEW_SELECTION_DIALOG';
const { CancelAndHelpDialog } = require('./cancelAndHelpDialog');
const CHOICE_PROMPT = 'CHOICE_PROMPT';
const WATERFALL_DIALOG = 'WATERFALL_DIALOG';
class ReviewSelectionDialog extends CancelAndHelpDialog {
constructor() {
super(REVIEW_SELECTION_DIALOG);
// Define a "done" response for the company selection prompt.
this.doneOption = 'done';
// Define value names for values tracked inside the dialogs.
this.companiesSelected = 'value-companiesSelected';
// Define the company choices for the company selection prompt.
this.companyOptions = ['Adatum Corporation', 'Contoso Suites', 'Graphic Design Institute', 'Wide World Importers'];
this.addDialog(new ChoicePrompt(CHOICE_PROMPT));
this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
this.selectionStep.bind(this),
this.loopStep.bind(this)
]));
this.initialDialogId = WATERFALL_DIALOG;
}
async selectionStep(stepContext) {
// Continue using the same selection list, if any, from the previous iteration of this dialog.
const list = Array.isArray(stepContext.options) ? stepContext.options : [];
stepContext.values[this.companiesSelected] = list;
// Create a prompt message.
let message = '';
if (list.length === 0) {
message = `Please choose a company to review, or \`${ this.doneOption }\` to finish.`;
} else {
message = `You have selected **${ list[0] }**. You can review an additional company, or choose \`${ this.doneOption }\` to finish.`;
}
// Create the list of options to choose from.
const options = list.length > 0
? this.companyOptions.filter(function(item) { return item !== list[0]; })
: this.companyOptions.slice();
options.push(this.doneOption);
// Prompt the user for a choice.
return await stepContext.prompt(CHOICE_PROMPT, {
prompt: message,
retryPrompt: 'Please choose an option from the list.',
choices: options
});
}
async loopStep(stepContext) {
// Retrieve their selection list, the choice they made, and whether they chose to finish.
const list = stepContext.values[this.companiesSelected];
const choice = stepContext.result;
const done = choice.value === this.doneOption;
if (!done) {
// If they chose a company, add it to the list.
list.push(choice.value);
}
if (done || list.length > 1) {
// If they're done, exit and return their list.
return await stepContext.endDialog(list);
} else {
// Otherwise, repeat this dialog, passing in the list from this iteration.
return await stepContext.replaceDialog(REVIEW_SELECTION_DIALOG, list);
}
}
}
module.exports.ReviewSelectionDialog = ReviewSelectionDialog;
module.exports.REVIEW_SELECTION_DIALOG = REVIEW_SELECTION_DIALOG;
CancelAndHelpDialog
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const { InputHints } = require('botbuilder');
const { ComponentDialog, DialogTurnStatus } = require('botbuilder-dialogs');
/**
* This base class watches for common phrases like "help" and "cancel" and takes action on them
* BEFORE they reach the normal bot logic.
*/
class CancelAndHelpDialog extends ComponentDialog {
async onContinueDialog(innerDc) {
const result = await this.interrupt(innerDc);
if (result) {
return result;
}
return await super.onContinueDialog(innerDc);
}
async interrupt(innerDc) {
if (innerDc.context.activity.text) {
const text = innerDc.context.activity.text.toLowerCase();
switch (text) {
case 'help':
case '?': {
const helpMessageText = 'Show help about prompt: ' + innerDc.activeDialog.id;
await innerDc.context.sendActivity(helpMessageText, helpMessageText, InputHints.ExpectingInput);
return { status: DialogTurnStatus.waiting };
}
case 'cancel':
case 'quit': {
const cancelMessageText = 'Cancelling...';
await innerDc.context.sendActivity(cancelMessageText, cancelMessageText, InputHints.IgnoringInput);
return await innerDc.cancelAllDialogs();
}
}
}
}
}
module.exports.CancelAndHelpDialog = CancelAndHelpDialog;
I want to thank you for using the sample code because you've actually revealed a bug that I've reported here: https://github.com/microsoft/BotBuilder-Samples/issues/2457
The underlying problem here is that the dialogs library has two ways of stacking dialogs. Ordinarily, one dialog gets stacked on top of another dialog like this:
[ CHOICE_PROMPT ]
[ WATERFALL_DIALOG ]
However, component dialogs form a nested dialog stack that stacks inward rather than further upward:
[ REVIEW_SELECTION_DIALOG ]
[ TOP_LEVEL_DIALOG ]
[ MAIN_DIALOG ]
Since not all dialogs are component dialogs, the two ways combine to look like this:
[ CHOICE_PROMPT ]
[ WATERFALL_DIALOG ]
[ REVIEW_SELECTION_DIALOG ]
[ TOP_LEVEL_DIALOG ]
[ MAIN_DIALOG ]
I want to note that the order of this stack is not necessarily what you'd expect if you're used to writing hierarchical lists that look like this (with the most recently added item on the bottom):
MAIN_DIALOG
TOP_LEVEL_DIALOG
REVIEW_SELECTION_DIALOG
WATERFALL_DIALOG
CHOICE_PROMPT
Some people might not consider the second way of stacking actual stacking, since it's a parent-child relationship and not a stack. The reason I'm calling it a second way of stacking here is because of the conceptual similarity to a dialog stack. When you design your bot's dialogs, you have a choice about whether you want each new dialog to be added on top of the existing dialog stack or be nested as a child in an inner dialog stack. The two ways behave similarly because a component dialog ends when its last child dialog ends, so when you pop a dialog off of a stack the stack unravels outwards in much the same way as it unravels downwards. (Remember that new dialogs get added to the top of the stack so "downwards" here means from newer dialogs back to older dialogs, like the stack diagrams I started with.)
The "active dialog" is the dialog at the top of the stack. Since each component dialog has its own dialog set and dialog state and dialog stack and dialog context, each component dialog has a different idea of what the active dialog is. Because the active dialog is defined in terms of a specific dialog stack, when there are multiple dialog stacks the active dialog depends on who you ask.
This didn't cause a problem for you when you were looking for the active dialog in the innermost component dialog. But then you replaced that component dialog's child with the component dialog itself. After that, your (full) stack looked like this:
[ CHOICE_PROMPT ]
[ WATERFALL_DIALOG ]
[ REVIEW_SELECTION_DIALOG ]
[ REVIEW_SELECTION_DIALOG ]
[ TOP_LEVEL_DIALOG ]
[ MAIN_DIALOG ]
When your CancelAndHelpDialog tried to access the active dialog of its inner dialog context, it correctly returned a ReviewSelectionDialog because that was the only dialog on its stack. You wanted to return the choice prompt but that choice prompt was in the dialog stack of the child ReviewSelectionDialog and not the parent ReviewSelectionDialog.
The bug is that you should be replacing the waterfall dialog with itself rather than with the parent component dialog. So it could look like this:
return await stepContext.replaceDialog(WATERFALL_DIALOG, list);
Or like this:
return await stepContext.replaceDialog(this.initialDialogId, list);
Ultimately, this still hasn't answered a question that you may have meant to ask. Since you've seen that problems can arise when you get the active dialog in an intermediate dialog context, you may want a way to get the "real" innermost active dialog. This can be accomplished with some simple recursion:
function getInnermostActiveDialog(dc) {
const child = dc.child;
return child ? getInnermostActiveDialog(child) : dc.activeDialog;
}

Stacking of Context Menus in Electron

I am building an Electron based application that contains a grid containing unique rows. I would like a context-menu that is specific to each row. Here is an example:
Although this screen shot is cropped, you can see there are multiple rows and each row contains separate data. Since I'd like to right-click on a row and get a unique context menu, I have implemented electron-context-menu, which does work on the first right click, but then subsequent right-clicks causes a stacking effect of context menus.
Specifically, here is what happens:
I right click on Row-1 and the proper context menu shows up
I right click on Row-2 and a repeat of the context menu for Row-1 shows up then Row-2's context menu shows up. (Notice in the screen shot the context menu showing does not correspond to the row my mouse is over)
This repeats itself.
In React.JS, here is my listener, which collects the contextmenu object as needed by the electron-context-menu module:
handleContextMenu() {
this.props.contextMenu({
window: electron.remote.BrowserWindow.getFocusedWindow(),
prepend: (params, browserWindow) => [{
label: `Library Compare ${this.state.msn}`,
click: () => this.runLibCompare()
}],
append: (params, browserWindow) => [{
label: '---',
}]
})
};
Where this.props.contextMenu(...) perculates up the React.JS components to be fed into:
const contextMenu = eRequire('electron-context-menu');
I have done some massive debugging and I don't think the issue is the module. The module I am using essentially organizes the information about the context menu and then uses electron.remote functions and a menu.popup function which comes from electron internals. Here is a link to the specific line in github.
const menu = (electron.Menu || electron.remote.Menu).buildFromTemplate(menuTpl);
menu.popup(electron.remote ? electron.remote.getCurrentWindow() : win);
This call to menu.popup leads to this line in electron.
const remoteMemberFunction = function (...args) {
if (this && this.constructor === remoteMemberFunction) {
// Constructor call.
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args))
return metaToValue(ret)
} else {
// Call member function.
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args))
return metaToValue(ret)
}
}
So I see a call to ipcRender.sendSync -- however when I add debugging statements in ipcMain's receiver of those calls, I don't see any output!
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
try {
args = unwrapArgs(event.sender, args)
let obj = objectsRegistry.get(id)
if (obj == null) {
throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
}
callFunction(event, obj[method], obj, args)
} catch (error) {
event.returnValue = exceptionToMeta(error)
}
})
When I added debug statements to the above function, I didn't see any output. And that is where my search his a wall.
I am using electron 1.4.15. I know this issue should be resolvable, after-all the Atom IDE (which is electron based) does not have this issue even though it has multiple context menus.
I think there is some memory I need to clear somewhere, I just can't figure out how to clear the stack of previous context menus!
I solve this by first getting the target of the click using e.target. Then, depending on that, I call the corresponding contextmenu. If target hit is not in the list of targets for my app, I use a default contextmenu.
window.addEventListener(
"contextmenu",
e => {
e.preventDefault();
if (e.target.id === 'fullscr'){
console.log(e && e.target);
// e.preventDefault();
mymenu.popup(remote.getCurrentWindow());
}else{
editmenu.popup(remote.getCurrentWindow());
}
console.log(e.which);
},
false
);

Titanium mobile - Common JS. Objects values are lost when lauching a fireEvent

I'm dev an app into titanium mobile in javascript.
The dynamic menu insert each new object(id,text,...., page) into a loop for (var x in tab).
with thoses items, specifics views are made.
var items = [];
var menuIconsItem = require('view/module/menuIconsItem');
for(var i in itemTab) {
var page = itemTab[i].page;
items[i] = new menuIconsItem(itemTab[i]);
menuFirstLine.add(items[i]);
(function(itemsEvent) {
itemsEvent.addEventListener('click', function() {
Ti.App.fireEvent('test' +i, {
id : i
});
})
})(items[i]);
}
on the other controller side, i only get the last id reference.
If i = 0 to 5, i only get the last reference. The rest is undefined.
How could i do please?
First you have to set id for your menuIconsItem, I am taking button an an example here.
items[i] = Titanium.UI.createButton({
id:"button_"+i,
_index: i
})
Then do this:
(function(itemsEvent) {
itemsEvent.addEventListener('click', function(e) {
alert(e.source.id);
})
})(items[i]);

Resources