Change:
// This is a follow-up issue for , cc Thanks to the team to provide a centralized contribution point for the button. Here are some feedback for consideration. Show the keyboard shortcut in the dropdown menu? Considering another entry for (editor/title/debug)? In that case the user can just have one click if each contribution(run and debug) just has one entry. Just like what we have in the debug view, the title area can have a dropdown menu explicitly and VS Code can memorize the last choice. (But given the very limited area there, this might not be a good idea though) <img width="166" alt="Screen Shot 2021-02-26 at 8 41 08 AM" src="https://user-"
Our idea is that the menu is used for run- and debug-commands
//cc
Now if I want to run my code, I need to click twice :( Previously one click was enough. Can you add a setting that sets a default behavior for the button? If you click on it once, "Run Code" should be the default. Clicking twice is a pain.
The Java side also has a user mentioned about the
I'd love an option to revert back to the previous button. This new feature has not once worked properly for me because it closes just as fast as it opens by itself. Seems to be a bug that I have been tracking for the past few days.
Clicking twice really is a pain, would appreciate if a 'Select Default Option' was available for different languages. P.S. Happening in Python
"Happening in Python" : what do you mean? Do you have a link for more info?
When I'm trying to click the run button, the context menu pops up. A few days back it would just run the file with the coderunner extension. Now I have to select again and again whether to run using the python extension or coderunner extension. ! !
OK, I misunderstood. When you wrote "Happening in Python", I thought a solution for Python was under way. Yes, I also noticed this double-click problem and it also bothers me. I hope there will be a solution for this soon.
For those of you with the problem, are those two contributions to the new menu coming from a single extension or two extensions? If two separate extensions, is it feasible to disable one for the workspace?
I have the same problem with
Usually the Python extension's button is hidden if the Code Runner extension is enabled, and it was working fine. But now when you click on it, the context menu pops up. I hope to use the Code Runner extension but there is no way to remove the run button in the Python extension, I checked for it and found this. .
I suggest we agument toolbar submenu actions with some indicator that it's going to be a dropdown. This should help in the user's perception of what will happen when they click it. For this specific toolbar submenu action, the Run, maybe it's best to further change it to behave like debug: a UI piece with two click zones, one for the default and another for the dropdown which, when selected, would run and change the default.
Now that we have hover feedback on toolbar actions (via ) I think it now makes sense to explore what adding affordances to split buttons could look like to improve their usability. Here are a few concepts: <img width="500" src="https://user-"<img width="500" src="https://user-"!
Option 3
Vote for option 3. The background color indicates that the and are in one group, while the different background color indicates that they have different functionalities.
I just pushed the new that and I worked on to base: This powers the terminal's button in the same style: ! It's basically the 3rd option but with a negative left margin on the right button so that the 2 buttons are closer together like option 2, this is particularly important to help distinguish the related icon for this case: <img width="238" alt="Screen Shot 2021-04-22 at 4 55 39 AM" src="https://user-"
We might want to consider introducing a shortcut for the "default run action"; see
Not sure I understand... This is just a dynamic sub menu. Are you suggesting to add a keybinding to trigger a submenu (or its sole command if only one exists)?
Since the "split button" UI introduces the concept of a "default run action", it would be helpful if this "default action" would have a keybinding (and if only one run action exists this action would have the keybinding).
This feature request tries to improve the user experience for the recently contribution point "editor/title/run". ! There are two pain points with the current UI of the contribution point: if there are more than two action contributions, two clicks are needed to trigger an action: the first click opens the drop down menu and the second selects the action from the menu. Having to use two clicks is a pain... from the UI it is not obvious whether the "run" item is a single action or a dropdown with multiple actions. This makes the UI instable and requires close attention by the user in order to not forget the "second click". The new split button () looks like a good way to address the pain points. ! The split button has two click zones, one for the default (left) and another for the dropdown (right) which, when selected, would run and change the default. Behavior: with a single action contribution the action would become the default and the drop down would be disabled. with two or more action contributions we have two options for the default: no default action and the user is required to open the drop down to select and run an action. This action is then remembered as the new default, or the first contributed action becomes the default automatically. This ensures that the UI is stable: it is always possible to click on the "default". We might even want to introduce a keybinding for the "default".
Please go ahead on this and let me know where I can help
please provide code pointers
I haven't used the but my understanding is the following a dedicated primary action for that "default", use for that and place it in the -menu is an UI only solution, e.g custom rendering for an action. This is done with an (per menu) and my understanding is that for editors that is happening view item provider should follow a similar logic as to what's done in , e.g. create the submenu () now and feed it into the dropdown , remove the -menu from the editor title menu Once the command is there (step 1), I can help with step 3 and 4
The split button in action: !
raised the question whether selecting a drop down menu action should only select or select and execute the action. The former would again require two clicks. The latter only one but its behavior might be a bit surprising. For comparison here is the current behavior: !
I was just concerned if we have other places in our UI where selecting something also runs it. Since the debug dropdown behaves different maybe I am just used to that selection only selects. I would still slightly lean towards that selection should not run because: I think users will not often switch between the options It is cleaner and aligns better with the rest of our UI
Another thing to consider: if the actions in the drop down menu do not have different icons, then selecting them without executing them will not have any visual effect on the split button since the icon does not change. This is different from the debug config dropdown where the unique name of a configuration is shown.
I think the dropdown in the debug view is visually different enough that it won't cause that confusion: <img width="220" alt="CleanShot 2021-06-30 at 10 46 56 src="https://user-"Because we also use the same interaction of selecting a dropdown to perform an action in the terminal: <img width="288" alt="CleanShot 2021-06-30 at 10 48 07 src="https://user-"
I was not aware that we also do this in the terminal. Then I think we should go with this approach here also.
I've released the feature. Please try it in the next Insiders.
Looks good! Just one bug I found, arrows are appended if user select through dropdown https://user- Version: 1.59.0-insider (user setup) Commit: Date: 2021-07-13T05:12:59.073Z Electron: 12.0.13 Chrome: 89.0.4389.128 14.16.0 V8: 8.9.255.25-electron.0 OS: Windows_NT x64 10.0.
interesting. I'm not seeing this with Mock Debug... Will investigate with Java
Yes, I can reproduce with the Java extension. Workaround: add a to the Run/Debug commands in (see ). This "enablement" is good anyway because it disables the command while a debug or run is running. This avoids that multiple debug sessions are created unintentionally.
I've fixed the problem that you've spotted () Please verify with next Insiders.
I noticed some weird behavior with which title is shown on the button. To replicate: the from to use the contribution point: the extension and ensure that the button is currently showing "Do some refactoring". Using the "start a cat coding session" option in the dropdown does not cause the button show the text for "start cat coding session", but it just stays the same instead. , if you click the "start cat coding session" option again with the window still open, it now shows the right text on the button Version info: Version: 1.59.0-insider (user setup) Commit: Date: 2021-07-21T06:59:32.970Z Electron: 13.1.7 Chrome: 91.0.4472.124 14.16.0 V8: 9.1.269.36-electron.0 OS: Windows_NT x64 10.0.
Hi thank you for the fixing. Now the dropdown arrow works pretty well. There is another problem though, after switching the active run action via the arrow, then the run/debug button itself becomes unclickable(no response after clicking).
I can reproduce the problem you are seeing. I'll investigate...
The issue is a cyclic context JSON that gets serialized over to the extension host via rpc and results in an exception due to the cycle (cc I've a workaround and I could no longer reproduce the problem in the webview sample and in the Java extension.
Thank you I just played it in the newest insider, everything works great now!

Discussion:
*--------------------------------------------------------------------------------------------*/ import 'vs/css!./menuEntryActionViewItem'; <del> import { addDisposableListener, asCSSUrl, ModifierKeyEmitter } from 'vs/base/browser/dom'; import { IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; </del> <ins> import { addDisposableListener, asCSSUrl, ModifierKeyEmitter, append, EventType, $ } from 'vs/base/browser/dom'; import { IAction, IRunEvent, Separator, SubmenuAction } from 'vs/base/common/actions'; </ins> import { IDisposable, toDisposable, MutableDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; <del> import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction, Icon } from 'vs/platform/actions/common/actions'; </del> <ins> import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction, Icon, IMenuService } from 'vs/platform/actions/common/actions'; </ins> import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; <del> import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; </del> <ins> import { ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; </ins> import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { isWindows, isLinux, OS } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; <ins> import { Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; </ins> export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, primaryGroup?: string): IDisposable { const groups = menu.getActions(options);

} } <ins> class DropdownWithDefaultActionViewItem extends BaseActionViewItem { private _defaultAction: ActionViewItem; private _dropdown: DropdownMenuActionViewItem; private _container: HTMLElement | null = null; private _storageKey: string; get onDidChangeDropdownVisibility(): Event<boolean> { return this._dropdown.onDidChangeVisibility; } constructor( submenuAction: SubmenuItemAction, @IKeybindingService protected readonly _keybindingService: IKeybindingService, @INotificationService protected _notificationService: INotificationService, @IContextMenuService protected _contextMenuService: IContextMenuService, @IMenuService protected _menuService: IMenuService, @IInstantiationService protected _instaService: IInstantiationService, @IStorageService protected _storageService: IStorageService ) { super(null, submenuAction); this._storageKey = `${submenuAction.item.submenu._debugName}_lastActionId`; // determine default action let defaultAction: IAction | undefined; let defaultActionId = _storageService.get(this._storageKey, StorageScope.WORKSPACE); if (defaultActionId) { defaultAction = submenuAction.actions.find(a => defaultActionId === a.id); } if (!defaultAction) { defaultAction = submenuAction.actions[0]; } this._defaultAction = this._instaService.createInstance(MenuEntryActionViewItem, <MenuItemAction>defaultAction, undefined); this._dropdown = new DropdownMenuActionViewItem(submenuAction, submenuAction.actions, this._contextMenuService, { menuAsChild: true, classNames: ['codicon', 'codicon-chevron-down'] }); this._dropdown.actionRunner.onDidRun((e: IRunEvent) => { if (e.action instanceof MenuItemAction) { this.update(e.action); } }); } private update(lastAction: MenuItemAction): void { this._storageService.store(this._storageKey, lastAction.id, StorageScope.WORKSPACE, StorageTarget.USER); this._defaultAction.dispose(); this._defaultAction = this._instaService.createInstance(MenuEntryActionViewItem, lastAction, undefined); if (this._container) { this.render(this._container); } } override setActionContext(newContext: unknown): void { super.setActionContext(newContext); this._defaultAction.setActionContext(newContext); this._dropdown.setActionContext(newContext); } override render(container: HTMLElement): void { this._container = container; super.render(this._container); this._container.classList.add('monaco-dropdown-with-primary'); const primaryContainer = $('.action-container'); this._defaultAction.render(append(this._container, primaryContainer)); this._register(addDisposableListener(primaryContainer, EventType.KEY_DOWN, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.RightArrow)) { this._defaultAction.element!.tabIndex = -1; this._dropdown.focus(); event.stopPropagation(); } })); const dropdownContainer = $('.dropdown-action-container'); this._dropdown.render(append(this._container, dropdownContainer)); this._register(addDisposableListener(dropdownContainer, EventType.KEY_DOWN, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.LeftArrow)) { this._defaultAction.element!.tabIndex = 0; this._dropdown.setFocusable(false); this._defaultAction.element?.focus(); event.stopPropagation(); } })); } override focus(fromRight?: boolean): void { if (fromRight) { this._dropdown.focus(); } else { this._defaultAction.element!.tabIndex = 0; this._defaultAction.element!.focus(); } } override blur(): void { this._defaultAction.element!.tabIndex = -1; this._dropdown.blur(); this._container!.blur(); } override setFocusable(focusable: boolean): void { if (focusable) { this._defaultAction.element!.tabIndex = 0; } else { this._defaultAction.element!.tabIndex = -1; this._dropdown.setFocusable(false); } } override dispose() { this._defaultAction.dispose(); this._dropdown.dispose(); super.dispose(); } } </ins> /** * Creates action view items for menu actions or submenu actions. */ <del> export function createActionViewItem(instaService: IInstantiationService, action: IAction): undefined | MenuEntryActionViewItem | SubmenuEntryActionViewItem { </del> <ins> export function createActionViewItem(instaService: IInstantiationService, action: IAction): undefined | MenuEntryActionViewItem | SubmenuEntryActionViewItem | BaseActionViewItem { </ins> if (action instanceof MenuItemAction) { return instaService.createInstance(MenuEntryActionViewItem, action, undefined); } else if (action instanceof SubmenuItemAction) { <del> return instaService.createInstance(SubmenuEntryActionViewItem, action); </del> <ins> if (action.item.rememberDefaultAction) { return instaService.createInstance(DropdownWithDefaultActionViewItem, action); } else { return instaService.createInstance(SubmenuEntryActionViewItem, action); } </ins> } else { return undefined; }

when?: ContextKeyExpression; group?: 'navigation' | string; order?: number; <ins> rememberDefaultAction?: boolean;	// for dropdown menu: if true the last executed action is remembered as the default action </ins> } export function isIMenuItem(item: IMenuItem | ISubmenuItem): item is IMenuItem {

MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); <del> MenuRegistry.appendMenuItem(MenuId.EditorTitle, { submenu: MenuId.EditorTitleRun, title: { value: localize('run', "Run"), original: 'Run', }, icon: Codicon.run, group: 'navigation', order: -1 }); </del> <ins> MenuRegistry.appendMenuItem(MenuId.EditorTitle, { submenu: MenuId.EditorTitleRun, rememberDefaultAction: true, title: { value: localize('run', "Run or Debug..."), original: 'Run or Debug...', }, icon: Codicon.run, group: 'navigation', order: -1 }); </ins> interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; }